気象庁の地震情報JSON(公式APIではない)の読み方:list.jsonの略語キー、震源の座標、同じ地震の複数報
要点
- 一覧は1本のURL。
https://www.jma.go.jp/bosai/quake/data/list.jsonをキーなしでGETすると、約1か月分の地震情報が新しい順に入っています。 - 同じ地震に複数の報がある。 震度速報、震源に関する情報、震源・震度情報が別々の件として並びます。地震ID
eidでまとめます。 - 震源は1つの文字列。
codの+35.7+140.7-50000/は北緯35.7度、東経140.7度、深さ50kmです。 serは型が混ざる。 212件のうち184件は文字列("1"、"2")、28件は数値の0でした。並べ替えには使いません。- 詳細JSONは配列とオブジェクトが混ざる。 地域が1つだけだとオブジェクト、複数だと配列で入ります。
最近の地震をプログラムやAIエージェントで扱うなら、list.json を取得し、地震ID eid ごとにまとめて、最新の報から震源と最大震度を取り出します。 気象庁ホームページの地震情報の画面が読み込むファイルで、API仕様書は見つかりませんでした。キー名は ttl、anm、cod、maxi のような略語で、その意味は値と画面のスクリプトから読むしかありません。
2026年10月20日に、Node.js(依存なし)とcurlから一覧を3回、詳細JSONを3件、地図ページを1回取得しました。一覧には2026年9月20日から10月20日までの212件が入っていました。天気予報と観測値は、天気予報JSONの使い方とアメダスの観測値JSONの使い方で扱っています。
list.json の形
一覧は配列で、rdt(発表時刻)の新しい順に並んでいました。HTTPの応答は Content-Type: application/json、Content-Encoding: br、Cache-Control: max-age=60 で、展開後は205,382バイトでした。先頭の1件です(int は抜粋)。
{ "ctt": "20261020055328", "eid": "20261020055029", "rdt": "2026-10-20T05:53:00+09:00", "ttl": "震源・震度情報", "ift": "発表", "ser": "1", "at": "2026-10-20T05:50:00+09:00", "anm": "釧路沖", "acd": "190", "cod": "+42.4+144.7-30000/", "mag": "4.0", "maxi": "2", "int": [ { "code": "01", "maxi": "2", "city": [ { "code": "0120600", "maxi": "2" }, ... ] } ], "json": "20261020055328_20261020055029_VXSE5k_1.json", "en_ttl": "Earthquake and Seismic Intensity Information", "en_anm": "Off the Coast of Kushiro"}212件すべてが同じ16個のキーを持っていました。値から読んだ対応です。
| キー | 値の例 | 読み方 |
|---|---|---|
eid | 20261020055029 | 地震ID。同じ地震の報で共通 |
ttl | 震源・震度情報 | 情報の種類 |
rdt | 2026-10-20T05:53:00+09:00 | 発表時刻 |
ctt | 20261020055328 | 作成時刻(秒まで、日本時間) |
at | 2026-10-20T05:50:00+09:00 | 地震の発生時刻 |
anm / acd | 釧路沖 / 190 | 震央地名とそのコード |
cod | +42.4+144.7-30000/ | 震源の緯度・経度・高さ(m) |
mag | 4.0 | マグニチュード。文字列 |
maxi | 2 | 最大震度。文字列 |
int | 都道府県と市町村の配列 | 都道府県コード2桁、市町村コード7桁ごとの最大震度 |
ift / ser | 発表 / "1" | 発表の区分と番号。212件すべて「発表」 |
json | ..._VXSE5k_1.json | 詳細JSONのファイル名 |
情報の種類と、同じ地震の複数報
212件の ttl の内訳です。詳細JSONのファイル名の3番目の部分も、種類ごとに決まっていました。
| ttl | 件数 | ファイル名の符号 | 空の項目 |
|---|---|---|---|
| 震源・震度情報 | 181 | VXSE5k | なし |
| 震度速報 | 14 | VXSE51 | anm・cod・mag が空、int は都道府県だけ |
| 震源に関する情報 | 13 | VXSE52 | maxi が空、int が空配列 |
| 遠地地震に関する情報 | 3 | VXSE5e | maxi が空。cod に深さがない |
| 南海トラフ地震関連解説情報 | 1 | VYSE52 | cod・mag・maxi が空 |
気象庁の地震情報についてによると、震度速報は震度3以上で、地震発生約1分半後に震度3以上を観測した地域名(全国188地域)と揺れの検知時刻を速報します。震源に関する情報は震度3以上(津波警報等を発表した場合は発表しない)で、震源と規模を発表します。震源・震度情報は震度1以上などで、震源・規模・各地点の震度を発表します。
一覧の eid は183種類で、14の地震に複数の報がありました。2026年10月8日4時13分ころの千葉県北東部の地震では、次の順に並んでいます。
- 04:13地震の発生(at)千葉県北東部、M4.8、深さ約50km、最大震度4
- 04:15震度速報(VXSE51)maxi は 4。震源(anm・cod・mag)は空
- 04:16震源に関する情報(VXSE52)震源と M4.8 が入る。maxi は空
- 04:17震源・震度情報(VXSE5k、ser "1")震源と最大震度4、市町村ごとの震度がそろう
報によって欠ける項目が違うので、最新の1件だけを見ると、震源か震度のどちらかが空になる時間帯があります。 震源は cod が入っている最新の報から、震度は maxi が入っている最新の報から取ります。
報が更新される例もありました。9月29日4時45分ころの茨城県南部の地震は、震度速報が4時46分と4時47分の2件、震源・震度情報が4時49分(ser が "1")と4時54分("2")の2件でした。2報目では神奈川県(int のコード14)の市町村が38から52に増えていました。国外の地震では、10月10日の中米の地震のマグニチュードが、1報目の8.0から2報目で7.6に変わっていました。
ser は並べ替えに使えません。 212件のうち、"1" が182件、"2" が2件の文字列で、震度速報などの28件は数値の 0 でした。同じ地震の報の順番は、rdt と ctt で決めます。
cod・maxi・mag の読み方
cod は、緯度・経度・高さを符号付きでつないだ文字列です。高さはメートルで、負の値が深さになります。
+42.4+144.7-30000/ -> 北緯42.4度、東経144.7度、深さ30km+32.6+130.7+0/ -> 北緯32.6度、東経130.7度、深さ0km(212件中33件)+07.5-080.8/ -> 北緯7.5度、西経80.8度、深さなし(国外の地震)深さ0の件の詳細JSONも取得しましたが、Coordinate は同じ文字列だけで、説明の文はありませんでした。地図ページのスクリプトは、深さが5km未満なら「ごく浅い」、それ以外は「約○km」と表示していました。
maxi は文字列です。212件では "1"(126件)、"2"(41件)、"3"(20件)、"4"(8件)、空(17件)でした。地図ページのスクリプトは、ほかに次の値を扱っていました。
"5-" 震度5弱 "5+" 震度5強 "6-" 震度6弱 "6+" 震度6強 "7" 震度7"5@" 震度5弱以上未入電震度についてのとおり、震度は0から7までの10階級です。"5-" を数値に変換すると NaN になり、parseInt では5になって弱と強の区別が消えます。文字列のまま、対応表で順位を付けます。"5@" は、震度5弱以上と考えられるのに震度を入手できていない地点があることを表す値で、スクリプトの順位では震度4と5弱のあいだでした。
mag も文字列で、震度速報などの15件は空でした。地図ページのスクリプトは、「M8を超」を含む文字列を「M8超」、「不明」を含む文字列を「---」と表示する処理を持っていました。今回の212件には現れていませんが、数値に変換できない値として扱える形にしておきます。
詳細JSONは Control・Head・Body
json のファイル名を https://www.jma.go.jp/bosai/quake/data/ に付けると、詳細JSONが取れます。千葉県北東部の震源・震度情報(53,935バイト)と震度速報(1,996バイト)を取得しました。
Control・Head・Body の3つに分かれた形で、XMLを変換したような構造です。変換の名残で、同じキーが、要素が1つならオブジェクト、2つ以上なら配列で入っていました。 見出しの Head.Headline.Information[].Item[].Areas.Area は、震度4の千葉県北東部では1件のオブジェクト、震度3の茨城県南部と千葉県北西部では2件の配列でした。どちらでも配列として扱う関数を挟みます。
観測点の名前は、206地点のうち171地点の末尾に「*」が付いていました。同じJSONの Body.Comments.VarComment に「*印は気象庁以外の震度観測点についての情報です。」とあり、英語文は地方公共団体または防災科学技術研究所の観測点としています。名前を検索キーにするときは「*」を外して比べます。
// quake-detail.mjs — 保存した詳細JSONを要約する(通信しない)import { readFile } from 'node:fs/promises';// XMLから変換されたJSONは、要素が1つだとオブジェクト、2つ以上だと配列になる箇所があるconst arr = (v) => (v == null ? [] : Array.isArray(v) ? v : [v]);const INT_RANK = ['1', '2', '3', '4', '5@', '5-', '5+', '6-', '6+', '7'];
export function summarizeDetail(d) { const eq = d.Body.Earthquake; const byInt = {}; let stations = 0, otherAgency = 0; for (const pref of arr(d.Body.Intensity?.Observation?.Pref)) { for (const area of arr(pref.Area)) { for (const city of arr(area.City)) { (byInt[city.MaxInt] ??= []).push(city.Name); for (const st of arr(city.IntensityStation)) { stations++; if (st.Name.endsWith('*')) otherAgency++; } } } } // 見出しの地域リストは Area がオブジェクトの場合と配列の場合がある const headline = arr(d.Head.Headline.Information).flatMap((info) => arr(info.Item).map((item) => ({ kind: item.Kind.Name, areas: arr(item.Areas.Area).map((a) => a.Name) }))); return { title: d.Head.Title, reportDatetime: d.Head.ReportDateTime, eventId: d.Head.EventID, serial: d.Head.Serial, originTime: eq?.OriginTime, hypocenter: eq?.Hypocenter.Area.Name, coordinate: eq?.Hypocenter.Area.Coordinate, magnitude: eq?.Magnitude, maxInt: d.Body.Intensity?.Observation?.MaxInt, // 震度3以上は市町村名、それ未満は数だけ citiesByMaxInt: Object.fromEntries(Object.entries(byInt).map(([k, v]) => [k, INT_RANK.indexOf(k) >= INT_RANK.indexOf('3') ? v : `${v.length}市町村`])), stations, otherAgencyStations: otherAgency, comments: [d.Body.Comments?.ForecastComment?.Text, d.Body.Comments?.VarComment?.Text].filter(Boolean), headlineFirst: headline.slice(0, 2), };}const d = JSON.parse(await readFile(process.argv[2], 'utf8'));console.log(JSON.stringify(summarizeDetail(d), null, 2));$ node quake-detail.mjs 20261008041755_20261008041355_VXSE5k_1.json"title": "震源・震度情報", "reportDatetime": "2026-10-08T04:17:00+09:00", "serial": "1","hypocenter": "千葉県北東部", "coordinate": "+35.7+140.7-50000/", "magnitude": "4.8", "maxInt": "4","citiesByMaxInt": { "1": "85市町村", "2": "32市町村", "3": ["匝瑳市", "香取市", "山武市", "多古町", "芝山町", "成田市", "潮来市", "稲敷市"], "4": ["旭市"] },"stations": 206, "otherAgencyStations": 171,"comments": ["この地震による津波の心配はありません。", "*印は気象庁以外の震度観測点についての情報です。"],"headlineFirst": [ { "kind": "震度4", "areas": ["千葉県北東部"] }, { "kind": "震度3", "areas": ["茨城県南部", "千葉県北西部"] } ]Node.jsで地震ごとにまとめる
一覧を取得し、国内の地震の3種類だけを eid でまとめます。最大震度が指定以上の地震について、震源と最大震度を最新の報から取り出します。
// jma-quake.mjs — 依存なし(Node.js 18 以降の組み込み fetch)// 使い方: node jma-quake.mjs 3 (最大震度3以上の地震を、地震ごとに1件へまとめて表示)const UA = 'jma-quake-example/0.1 (+https://codeagent.jp/)';const BASE = 'https://www.jma.go.jp/bosai/quake/data';// 画面のスクリプトと同じ並び。"5@" は「震度5弱以上未入電」const INT_LABEL = { '1': '震度1', '2': '震度2', '3': '震度3', '4': '震度4', '5-': '震度5弱', '5+': '震度5強', '6-': '震度6弱', '6+': '震度6強', '7': '震度7', '5@': '震度5弱以上未入電' };const INT_RANK = ['1', '2', '3', '4', '5@', '5-', '5+', '6-', '6+', '7'];const KINDS = ['震度速報', '震源に関する情報', '震源・震度情報']; // 国内の地震の3種類だけを扱う
async function getJson(url) { const res = await fetch(url, { headers: { 'User-Agent': UA } }); const type = res.headers.get('content-type') ?? ''; if (!res.ok || !type.includes('json')) throw new Error(`取得失敗: ${url} -> HTTP ${res.status} (${type})`); return res.json();}
// "+35.7+140.7-50000/" → 緯度・経度・深さ(km)。深さが無い形もあるfunction parseCod(cod) { const m = /^([+-]\d+(?:\.\d+)?)([+-]\d+(?:\.\d+)?)([+-]\d+)?\/$/.exec(cod ?? ''); if (!m) return null; const depthKm = m[3] === undefined ? null : -Number(m[3]) / 1000; return { lat: Number(m[1]), lon: Number(m[2]), depthKm, depthLabel: depthKm === null ? null : depthKm < 5 ? 'ごく浅い' : `約${depthKm}km` };}
const min = process.argv[2] ?? '3';const list = await getJson(`${BASE}/list.json`);const retrievedAt = new Date().toISOString();
// eid(地震ID)でまとめる。同じ地震に震度速報・震源・震源震度の複数報があるconst events = new Map();for (const r of list) { if (!KINDS.includes(r.ttl)) continue; const e = events.get(r.eid) ?? { eid: r.eid, reports: [] }; e.reports.push(r); events.set(r.eid, e);}
const out = [];for (const e of events.values()) { // 新しい報を先に。rdt が同じなら作成時刻 ctt で比べる。ser は文字列と数値が混ざるので使わない e.reports.sort((a, b) => (b.rdt + b.ctt).localeCompare(a.rdt + a.ctt)); const withHypo = e.reports.find((r) => r.cod); const withInt = e.reports.find((r) => r.maxi); if (!withInt || INT_RANK.indexOf(withInt.maxi) < INT_RANK.indexOf(min)) continue; const mag = withHypo?.mag ?? ''; out.push({ eid: e.eid, originTime: e.reports[0].at, area: withHypo?.anm || null, // 震度速報しか出ていなければ震源は未発表 hypocenter: parseCod(withHypo?.cod), magnitude: /^\d+(\.\d+)?$/.test(mag) ? Number(mag) : null, magnitudeText: mag || null, // "M8を超える巨大地震" のような文字列もありうる maxIntensity: INT_LABEL[withInt.maxi] ?? withInt.maxi, latestReport: { title: e.reports[0].ttl, reportDatetime: e.reports[0].rdt }, reportTitles: e.reports.map((r) => `${r.ttl}(${r.rdt.slice(11, 16)})`), detailUrl: `${BASE}/${(e.reports.find((r) => r.ttl === '震源・震度情報') ?? e.reports[0]).json}`, });}
console.log(JSON.stringify({ listEntries: list.length, events: out.length, items: out.slice(0, 5), provenance: { source: `${BASE}/list.json`, retrievedAt, attribution: '出典:気象庁ホームページ(https://www.jma.go.jp/bosai/map.html#contents=earthquake_map)を加工して作成', warnings: ['気象庁ホームページが内部で使うJSON。API仕様としては公開されておらず、予告なく変わりうる'], },}, null, 2));10時35分に実行した結果です(5件のうち2件目を抜粋)。
$ node jma-quake.mjs 3"listEntries": 212, "events": 13,{ "eid": "20261008041355", "originTime": "2026-10-08T04:13:00+09:00", "area": "千葉県北東部", "hypocenter": { "lat": 35.7, "lon": 140.7, "depthKm": 50, "depthLabel": "約50km" }, "magnitude": 4.8, "magnitudeText": "4.8", "maxIntensity": "震度4", "latestReport": { "title": "震源・震度情報", "reportDatetime": "2026-10-08T04:17:00+09:00" }, "reportTitles": ["震源・震度情報(04:17)", "震源に関する情報(04:16)", "震度速報(04:15)"], "detailUrl": "https://www.jma.go.jp/bosai/quake/data/20261008041755_20261008041355_VXSE5k_1.json" }"provenance": { "source": "https://www.jma.go.jp/bosai/quake/data/list.json", "retrievedAt": "2026-10-20T01:35:32.838Z", ... }約1か月分の212件が、最大震度3以上の13の地震にまとまりました。震源・震度情報がまだ出ていない地震では、area と hypocenter が null のまま、震度だけを返します。
AIエージェント・MCPツールにするときの設計
- 地震ごとにまとめてから渡す。 一覧をそのまま渡すと、同じ地震の3報を3つの地震と数えます。
eidでまとめ、どの報から取った値かをlatestReportとreportTitlesで残します。 - 値の型を固定する。
maxiは文字列のまま対応表で順位を付け、magは数値と元の文字列の両方を返します。serは使いません。 - 発表時刻と出典を返す。 地震の発生時刻(
at)と発表時刻(rdt)を分け、取得元URLと取得日時を付けます。型はMCPの出典情報を欠落させないoutputSchema設計に合わせられます。 - 取得の間隔はサーバー側で決める。 一覧は
max-age=60の間は取り直しません。詳細JSONは必要な地震だけ取得します。考え方は公共データAPIをMCP化する設計パターンにあります。 - 発表された内容の範囲だけを答えさせる。 余震の見通しや被害の推測を作らせません。気象庁ホームページについてのページは、気象業務法第17条(予報業務の許可)と第23条(警報の制限)による制約に注意を促しています。
- 地震の回答は get_recent_quakes の結果だけを根拠にし、発生時刻・発表時刻・出典を併記する- 震源が null の地震は「震源は未発表」と書き、震度から震源を推測しない- 「*」の付いた観測点は気象庁以外の震度計であることを、地点名を出すときに残す- 今後の揺れや被害の見通しを作らない。行動の判断は気象庁・自治体の発表を確認するよう返す- 出典は「出典:気象庁ホームページ(URL)を加工して作成」の形で残すまとめ
- 一覧は
quake/data/list.json。キー不要でGETのみ。2026年10月20日は約1か月分の212件 - 同じ地震に震度速報・震源に関する情報・震源・震度情報が並ぶ。
eidでまとめ、震源と震度は別々の報から取る codは緯度・経度・高さ(m)。深さ0は33件、画面は5km未満を「ごく浅い」と表示maxi・magは文字列。serは文字列と数値が混ざるので並べ替えに使わない- 詳細JSONは Control・Head・Body。
Areaは1件ならオブジェクト、複数なら配列
ほかの公共データとの使い分けは公共データAPIのガイドにあります。
この記事の情報・検証メモ
- 公開日
- 情報確認
- 参考リンク
- 4件
- 更新性
- 定期更新
仕様・料金・提供範囲が変わりやすいテーマは、公開日・更新日・情報確認日を分けて管理します。 導入前には必ず記事末尾の一次情報と公式ドキュメントで最新状況を確認してください。
一次情報・参考リンク
- 気象庁: 地震情報(地図ページ。2026-10-20にページと list.json・詳細JSONを取得) https://www.jma.go.jp/bosai/map.html#contents=earthquake_map
- 気象庁: 地震情報について(情報の種類・発表基準) https://www.jma.go.jp/jma/kishou/know/jishin/joho/seisinfo.html
- 気象庁: 震度について(震度階級) https://www.jma.go.jp/jma/kishou/know/shindo/index.html
- 気象庁ホームページについて(利用規約・リンク・利用上の注意) https://www.jma.go.jp/jma/kishou/info/coment.html