気象庁の天気予報JSONをAIエージェントで使う:地域コード・予報の構造・利用条件
気象庁ホームページが内部で使う天気予報JSON(area.json・forecast・overview_forecast)を2026年9月14日にNode.jsで取得して確認。地域コードの引き方、timeSeriesの読み方、週間予報の区分のずれ、利用条件と出典、MCPツール化の設計まで。
気象庁の天気予報をAIエージェントから引くなら、地域名を area.json で府県予報区のコードに変換し、forecast/ 配下の <コード>.json を取得すれば、キーなしで気象庁の発表内容がそのまま手に入ります。 ただし気象庁ホームページの画面が内部で読み込むJSONで、API仕様書は見つかりませんでした。発表時刻と出典を必ず残し、構造が想定と違えば止まる実装にします。
2026年9月14日にNode.js(依存なし)から合計9回リクエストし、実物のキー名を確かめました。住所から市区町村コードを引く側は国土地理院の住所検索APIと逆ジオコーダーの使い方にまとめています。
先に押さえること
- URLは3種類。
area.json(地域コード表)、forecast/<府県予報区コード>.json(3日分と週間)、overview_forecast/<同>.json(天気概況)です。 - 予報のファイル名は offices のコード。 東京都は
130000です。区域のコード130010を渡すと404のHTMLが返りました。 - 週間予報は地域区分が別。 東京都は
130100(area.json に存在しない)、茨城県は県全体の080000の1区分でした。 - 発表時刻と出典を返す。
reportDatetimeと取得元URLを結果に含め、空文字はnullにします。
取得した3つのJSON
| 用途 | URL | 実測サイズ | Last-Modified(GMT) |
|---|---|---|---|
| 地域コード表 | https://www.jma.go.jp/bosai/common/const/area.json | 262,108 bytes | 2026-09-14 04:10:33 |
| 天気予報(東京都) | https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json | 5,550 bytes | 2026-09-14 07:41:33 |
| 天気概況(東京都) | https://www.jma.go.jp/bosai/forecast/data/overview_forecast/130000.json | 994 bytes | 2026-09-14 07:37:52 |
3つとも Content-Type: application/json、Cache-Control: max-age=60、Access-Control-Allow-Origin: * で、キーなしのGETだけで返りました。
地域コードの引き方:area.jsonの5階層
area.json のトップレベルは centers、offices、class10s、class15s、class20s の5つです。各エントリは name・enName を持ち、parent と children で親子をたどれます(centers には parent がなく、class20s は children の代わりに kana を持ちます)。
市区町村名から予報のファイルまでは、parent を3回たどります。
class20s 1310100 千代田区 -> class15s 130011 23区西部 -> class10s 130010 東京地方 ← 予報JSONの areas で使うコード -> offices 130000 東京都 ← forecast/130000.json
class20s 0822000 つくば市 -> class15s 080022 県南地域 -> class10s 080020 南部 -> offices 080000 茨城県 ← forecast/080000.json引っかかった点は4つです。
- offices は都道府県と一致しない。 北海道は宗谷地方(
011000)など8つ、沖縄県は4つ、鹿児島県は奄美地方(460040)とそれ以外(460100)に分かれます。 - 区域のコードでは取れない。
forecast/130010.jsonとforecast/999999.jsonはどちらも HTTP 404 のtext/htmlでした。res.json()の前にContent-Typeを確かめます。 - 同名がある。 「府中市」は
1320600(東京都)と3420800(広島県)の2件で、部分一致なら「府中町」も当たります。 - class20s の先頭5桁は市区町村コード。 1,805件すべての先頭5桁が、地理院地図の市区町村コード表に存在しました(末尾が
00以外の89件は「横浜市北部」のような分割区域)。逆ジオコーダーの結果から予報区へ進めます。
予報JSONの構造:配列2要素とtimeSeries
forecast/130000.json は2要素の配列でした(2026年9月14日17時発表分)。
[0] 3日分 publishingOffice, reportDatetime, timeSeries (3) timeSeries[0] timeDefines 3つ areas: class10 weatherCodes, weathers, winds, waves timeSeries[1] timeDefines 5つ areas: class10 pops timeSeries[2] timeDefines 2つ areas: 地点 temps[1] 週間 publishingOffice, reportDatetime, timeSeries (2), tempAverage, precipAverage timeSeries[0] timeDefines 7つ areas: 週間区域 weatherCodes, pops, reliabilities timeSeries[1] timeDefines 7つ areas: 地点 tempsMin, tempsMinUpper, tempsMinLower, tempsMax, tempsMaxUpper, tempsMaxLower東京地方の timeSeries[0] と timeSeries[1] の実物です。
{ "area": { "name": "東京地方", "code": "130010" }, "weatherCodes": ["200", "302", "302"], "weathers": [ "くもり 所により 夜 雨", "雨 時々 くもり 所により 雷 を伴う", "雨 時々 くもり" ], "winds": ["南の風 やや強く", "南の風 23区西部 では 南の風 やや強く", "北東の風 後 やや強く"], "waves": ["1.5メートル", "1メートル 後 0.5メートル", "0.5メートル 後 1メートル"]}{ "area": { "name": "東京地方", "code": "130010" }, "pops": ["30", "60", "50", "50", "50"] }読むときのルールです。
- 値は同じ
timeSeriesのtimeDefinesと添字で対応する。timeSeries[0]は日単位の3つ、timeSeries[1]は18時から6時間ごとの5つで、刻みが違います。 - 数値も文字列で、週間予報には空文字がある。 東京地方の週間
popsは["", "80", "50", ...]、reliabilitiesは["", "", "C", ...]でした。0にせずnullにします。 - 週間予報の地域区分は3日分と別。 東京都の週間には area.json にない
130100(伊豆諸島)があり、茨城県は080000の1区分だけでした。 - 気温の地点コードは area.json にない。
tempsの地点は「東京」44132などで、2つの値("24"、"28")のどちらが最低か最高かを示すキーはありません。意味を決めつけてモデルに渡しません。 - 天気コードの対応表はJSONの外。 週間は
weatherCodesだけで、対応表は予報ページのHTMLに埋め込まれたTELOPS(118件、200は曇、302は雨時々止む)にありました。同じ302でも3日分のweathersは「雨 時々 くもり」なので、文言をコードから組み立て直しません。
天気概況(overview_forecast/130000.json)は publishingOffice、reportDatetime、targetArea、headlineText、text の5キーで、reportDatetime は予報の17時と違う16時36分でした。両方を別々に保持します。
Node.jsで取得する(依存なし)
地域名から候補を返し、1件に決まったときだけ予報を取得します。area.json は1日キャッシュします。
// jma-weather.mjs — 依存なし(Node.js 18 以降の組み込み fetch)// 使い方: node jma-weather.mjs つくば市import { readFile, writeFile, mkdir, stat } from 'node:fs/promises';
const UA = 'jma-weather-example/0.1 (+https://codeagent.jp/)';const BASE = 'https://www.jma.go.jp/bosai';const AREA_URL = `${BASE}/common/const/area.json`;const AREA_CACHE = './cache/area.json';const AREA_TTL_MS = 24 * 60 * 60 * 1000; // 地域コード表は1日キャッシュ
async function getJson(url) { const res = await fetch(url, { headers: { 'User-Agent': UA } }); const type = res.headers.get('content-type') ?? ''; // 存在しないコードは 404 の HTML が返るので、content-type も確認する if (!res.ok || !type.includes('json')) { throw new Error(`取得失敗: ${url} -> HTTP ${res.status} (${type})`); } return res.json();}
async function loadArea() { try { const s = await stat(AREA_CACHE); if (Date.now() - s.mtimeMs < AREA_TTL_MS) { return JSON.parse(await readFile(AREA_CACHE, 'utf8')); } } catch { /* キャッシュなし */ } const body = await getJson(AREA_URL); await mkdir('./cache', { recursive: true }); await writeFile(AREA_CACHE, JSON.stringify(body)); return body;}
// 市区町村名(class20s)から候補を返す。複数あっても自動で1件に絞らないfunction resolveArea(area, name) { const out = []; for (const [code, c20] of Object.entries(area.class20s)) { if (c20.name !== name) continue; const c15 = area.class15s[c20.parent]; const c10 = area.class10s[c15.parent]; out.push({ class20: code, name: c20.name, class10: c15.parent, class10Name: c10.name, office: c10.parent, officeName: area.offices[c10.parent].name, }); } return out;}
const blank = (v) => (v === '' ? null : v); // 空文字を 0 や "0" にしない
const input = process.argv[2];const area = await loadArea();const candidates = resolveArea(area, input);if (candidates.length !== 1) { console.log(JSON.stringify({ status: candidates.length ? 'ambiguous' : 'not_found', input, candidates, }, null, 2)); process.exit(0);}
const target = candidates[0];const forecastUrl = `${BASE}/forecast/data/forecast/${target.office}.json`;const overviewUrl = `${BASE}/forecast/data/overview_forecast/${target.office}.json`;const retrievedAt = new Date().toISOString();const [short, weekly] = await getJson(forecastUrl);const overview = await getJson(overviewUrl);
const [weatherTs, popTs] = short.timeSeries;const w = weatherTs.areas.find((a) => a.area.code === target.class10);const p = popTs.areas.find((a) => a.area.code === target.class10);if (!w || !p) throw new Error(`予報JSONに ${target.class10} が見つからない`);const wk = weekly?.timeSeries[0].areas.find((a) => a.area.code === target.class10);
console.log(JSON.stringify({ status: 'ok', input, resolved: target, data: { area: w.area, weather: weatherTs.timeDefines.map((time, i) => ({ time, code: w.weatherCodes[i], text: w.weathers[i] })), pops: popTs.timeDefines.map((time, i) => ({ time, pop: blank(p.pops[i]) })), weekly: wk ? { matched: true, days: weekly.timeSeries[0].timeDefines.map((time, i) => ({ time, code: wk.weatherCodes[i], pop: blank(wk.pops[i]), reliability: blank(wk.reliabilities[i]) })) } : { matched: false, weeklyAreas: weekly?.timeSeries[0].areas.map((a) => a.area) ?? [] }, overviewHead: overview.text.trim().slice(0, 40), }, provenance: { publisher: short.publishingOffice, reportDatetime: short.reportDatetime, overviewReportDatetime: overview.reportDatetime, retrievedAt, sources: [forecastUrl, overviewUrl, AREA_URL], attribution: '出典:気象庁ホームページ(https://www.jma.go.jp/bosai/forecast/)を加工して作成', warnings: ['気象庁ホームページが内部で使うJSON。API仕様としては公開されておらず、予告なく変わりうる'], },}, null, 2));「府中市」では area.json だけを取得し、2件の候補で止まりました。
$ node jma-weather.mjs 府中市{ "status": "ambiguous", "input": "府中市", "candidates": [ { "class20": "1320600", "name": "府中市", "class10": "130010", "class10Name": "東京地方", "office": "130000", "officeName": "東京都" }, { "class20": "3420800", "name": "府中市", "class10": "340010", "class10Name": "南部", "office": "340000", "officeName": "広島県" } ] }「つくば市」はキャッシュ済みの area.json を使い、予報と概況の2回だけ取得しました(長い配列は抜粋)。
$ node jma-weather.mjs つくば市"resolved": { "class20": "0822000", "class10": "080020", "class10Name": "南部", "office": "080000", "officeName": "茨城県" }"weather": [ { "time": "2026-09-14T17:00:00+09:00", "code": "111", "text": "晴れ 夜 くもり 所により 夜遅く 雨" }, { "time": "2026-09-15T00:00:00+09:00", "code": "300", "text": "雨 所により 雷 を伴う" }, { "time": "2026-09-16T00:00:00+09:00", "code": "302", "text": "雨 時々 くもり" } ]"pops": [ { "time": "2026-09-14T18:00:00+09:00", "pop": "20" }, … { "time": "2026-09-15T18:00:00+09:00", "pop": "70" } ]"weekly": { "matched": false, "weeklyAreas": [ { "name": "茨城県", "code": "080000" } ] }"provenance": { "publisher": "水戸地方気象台", "reportDatetime": "2026-09-14T17:00:00+09:00", "overviewReportDatetime": "2026-09-14T16:36:00+09:00", "retrievedAt": "2026-09-14T12:39:52.415Z", "sources": [ ".../forecast/data/forecast/080000.json", ".../overview_forecast/080000.json", ".../common/const/area.json" ] }発表主体は、東京都の「気象庁」に対して茨城県は「水戸地方気象台」でした。週間予報は区域コード 080020 では見つからず、matched: false で県全体の区分を返しています。週間の区分へ黙って読み替えず、区域が違うことを結果に残すのが要点です。
AIエージェント・MCPツールにするときの設計
止まると困る用途なら、気象庁が案内している配信手段と比べてから決めます。
JSONで作るなら、次の5つを決めておきます。
- ツールを2つに分ける。
resolve_jma_area(name)は候補一覧だけ、get_jma_forecast(class20Code)はコードだけを受け付けます。同名をモデルの推測で1件に決めさせないためです。 - 発表時刻を出典として返す。
reportDatetime、publishingOffice、取得元URL、取得日時を別フィールドにします。型はMCPの出典情報を欠落させないoutputSchema設計に合わせられます。 - 構造を検証し、違えば止める。 配列であること、
timeDefinesと値の長さ、目的の区域コードの有無を確かめ、推測で埋めません。 - キャッシュと間隔はサーバー側に置く。
area.jsonは日単位、予報も少なくともmax-age=60の間は取り直しません。上限や待機をモデルに任せない理由は公共データAPIをMCP化する設計パターンに書きました。 - 発表内容の範囲だけを答えさせる。 第17条は気象庁以外の者が予報の業務を行う場合に気象庁長官の許可を求め、第23条は気象庁以外の者の警報を原則として禁じています。筆者は気象や法律の専門家ではないため、どこからが予報業務に当たるかは第17条のページから案内されている「予報業務の許可について」で確認してください。
- 天気の回答は get_jma_forecast の結果だけを根拠にし、reportDatetime と出典を併記する- 地域の候補が複数なら候補を示して止まる(推測で選ばない)- 値が null の日は「発表なし」と書き、前後の日から補わない- 取得結果にない予想や、独自の警戒の呼びかけを作らない- 出典は「出典:気象庁ホームページ(URL)を加工して作成」の形で残すまとめ
- 予報は
forecast/<府県予報区コード>.json、概況はoverview_forecast/、地域コード表はarea.json。キー不要でGETのみ - ファイル名は
officesのコード。区域コードは404のHTMLになり、同名の市は候補を返して止める - 値は
timeDefinesと添字で対応し、数値は文字列、週間の空欄は空文字。週間の地域区分は3日分とずれ、天気コードの対応表はJSONの外にある - 公式API仕様はなく予告なく変わりうる。出典と
reportDatetimeを残し、発表内容を超えた予想を作らせない
どのAPIを選ぶかの全体像は日本の公共データAPI 5選にあります。手続不要で使える公共データの別の例として、国会会議録検索APIの使い方も参考になります。
この記事の情報・検証メモ
- 公開日
- 情報確認
- 参考リンク
- 8件
- 更新性
- 定期更新
仕様・料金・提供範囲が変わりやすいテーマは、公開日・更新日・情報確認日を分けて管理します。 導入前には必ず記事末尾の一次情報と公式ドキュメントで最新状況を確認してください。
一次情報・参考リンク
- 気象庁ホームページについて(利用規約・リンク・利用上の注意) https://www.jma.go.jp/jma/kishou/info/coment.html
- 気象庁: 気象業務法「第十七条」(予報業務の許可) https://www.jma.go.jp/jma/kishou/info/ml-17.html
- 気象庁: 気象業務法「第二十三条」(警報の制限) https://www.jma.go.jp/jma/kishou/info/ml-23.html
- デジタル庁: 公共データ利用規約(第1.0版) https://www.digital.go.jp/resources/open_data/public_data_license_v1.0
- 気象庁: 気象データ高度利用ポータルサイト https://www.data.jma.go.jp/developer/index.html
- 気象庁防災情報XMLフォーマット形式電文の公開(PULL型) https://xml.kishou.go.jp/xmlpull.html
- 気象庁ホームページ: area.json(2026-09-14取得) https://www.jma.go.jp/bosai/common/const/area.json
- 気象庁ホームページ: 東京都の予報JSON(2026-09-14取得) https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json