本文へスキップ

気象庁の天気予報JSONをAIエージェントで使う:地域コード・予報の構造・利用条件

気象庁ホームページが内部で使う天気予報JSON(area.json・forecast・overview_forecast)を2026年9月14日にNode.jsで取得して確認。地域コードの引き方、timeSeriesの読み方、週間予報の区分のずれ、利用条件と出典、MCPツール化の設計まで。

SHAYOUWORLD 更新 約6分

気象庁の天気予報をAIエージェントから引くなら、地域名を area.json で府県予報区のコードに変換し、forecast/ 配下の <コード>.json を取得すれば、キーなしで気象庁の発表内容がそのまま手に入ります。 ただし気象庁ホームページの画面が内部で読み込むJSONで、API仕様書は見つかりませんでした。発表時刻と出典を必ず残し、構造が想定と違えば止まる実装にします。

2026年9月14日にNode.js(依存なし)から合計9回リクエストし、実物のキー名を確かめました。住所から市区町村コードを引く側は国土地理院の住所検索APIと逆ジオコーダーの使い方にまとめています。

先に押さえること

  1. URLは3種類。 area.json(地域コード表)、forecast/<府県予報区コード>.json(3日分と週間)、overview_forecast/<同>.json(天気概況)です。
  2. 予報のファイル名は offices のコード。 東京都は 130000 です。区域のコード 130010 を渡すと404のHTMLが返りました。
  3. 週間予報は地域区分が別。 東京都は 130100(area.json に存在しない)、茨城県は県全体の 080000 の1区分でした。
  4. 発表時刻と出典を返す。 reportDatetime と取得元URLを結果に含め、空文字は null にします。

取得した3つのJSON

用途URL実測サイズLast-Modified(GMT)
地域コード表https://www.jma.go.jp/bosai/common/const/area.json262,108 bytes2026-09-14 04:10:33
天気予報(東京都)https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json5,550 bytes2026-09-14 07:41:33
天気概況(東京都)https://www.jma.go.jp/bosai/forecast/data/overview_forecast/130000.json994 bytes2026-09-14 07:37:52

3つとも Content-Type: application/jsonCache-Control: max-age=60Access-Control-Allow-Origin: * で、キーなしのGETだけで返りました。

地域コードの引き方:area.jsonの5階層

area.json のトップレベルは centersofficesclass10sclass15sclass20s の5つです。各エントリは nameenName を持ち、parentchildren で親子をたどれます(centers には parent がなく、class20schildren の代わりに kana を持ちます)。

58件
offices
予報JSONのファイル単位。東京都は130000
142件
class10s
「東京地方」「伊豆諸島北部」などの区域
375件
class15s
「23区西部」などのまとまり
1,805件
class20s
「千代田区」「つくば市」などの市区町村単位
https://www.jma.go.jp/bosai/common/const/area.json(2026-09-14取得)のエントリ数

市区町村名から予報のファイルまでは、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.jsonforecast/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"] }

読むときのルールです。

  1. 値は同じ timeSeriestimeDefines と添字で対応する。 timeSeries[0] は日単位の3つ、timeSeries[1] は18時から6時間ごとの5つで、刻みが違います。
  2. 数値も文字列で、週間予報には空文字がある。 東京地方の週間 pops["", "80", "50", ...]reliabilities["", "", "C", ...] でした。0 にせず null にします。
  3. 週間予報の地域区分は3日分と別。 東京都の週間には area.json にない 130100(伊豆諸島)があり、茨城県は 080000 の1区分だけでした。
  4. 気温の地点コードは area.json にない。 temps の地点は「東京」44132 などで、2つの値("24""28")のどちらが最低か最高かを示すキーはありません。意味を決めつけてモデルに渡しません。
  5. 天気コードの対応表はJSONの外。 週間は weatherCodes だけで、対応表は予報ページのHTMLに埋め込まれた TELOPS(118件、200 は曇、302 は雨時々止む)にありました。同じ 302 でも3日分の weathers は「雨 時々 くもり」なので、文言をコードから組み立て直しません。

天気概況(overview_forecast/130000.json)は publishingOfficereportDatetimetargetAreaheadlineTexttext の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
防災情報XML(PULL型)
案内の場所
気象データ高度利用ポータルに記載なし
気象データ高度利用ポータルから案内
仕様
仕様書は見つからず、実物から構造を読む
仕様や電文ごとの解説資料を理解した上での利用を求めている
更新の知り方
ファイルを取り直す(Cache-Control は max-age=60)
高頻度フィードは毎分、長期フィードは毎時更新
注意事項
予告なく内容・URLが変わりうる
配信の停止・遅延がありうる。1日10GB以上のダウンロードは接続元IPを遮断
各ページと取得結果を2026-09-14に確認

JSONで作るなら、次の5つを決めておきます。

  1. ツールを2つに分ける。 resolve_jma_area(name) は候補一覧だけ、get_jma_forecast(class20Code) はコードだけを受け付けます。同名をモデルの推測で1件に決めさせないためです。
  2. 発表時刻を出典として返す。 reportDatetimepublishingOffice、取得元URL、取得日時を別フィールドにします。型はMCPの出典情報を欠落させないoutputSchema設計に合わせられます。
  3. 構造を検証し、違えば止める。 配列であること、timeDefines と値の長さ、目的の区域コードの有無を確かめ、推測で埋めません。
  4. キャッシュと間隔はサーバー側に置く。 area.json は日単位、予報も少なくとも max-age=60 の間は取り直しません。上限や待機をモデルに任せない理由は公共データAPIをMCP化する設計パターンに書きました。
  5. 発表内容の範囲だけを答えさせる。 第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の使い方も参考になります。

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

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

検証メモ
Node.js 24.8.0(組み込み fetch、依存なし) 気象庁ホームページの予報JSON・予報ページ 取得日 2026-09-14(計9リクエスト)
図解を保存・共有

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

気象庁の天気予報JSONをAIエージェントで使う:地域コード・予報の構造・利用条件 予報JSONは地域コードで引ける。発表時刻と出典を残し、公式APIでない前提で使う 地域コード:area.jsonは5階層、予報のファイル名はofficesのコード。区域コード130010を渡すと404のHTMLが返る。府中市のような同名の市は候補を返して止まる。 JSONの構造:配列の先頭が3日分、2番目が週間予報。値は文字列で、週間予報の空欄は空文字。週間予報の地域区分は3日分と一致しない。 ツール化の前提:reportDatetimeと出典URLを必ず返す。API仕様はなく、予告なく変わりうる。発表内容の範囲を超えた予想を作らせない。
気象庁の天気予報JSONをAIエージェントで使う:地域コード・予報の構造・利用条件 記事の要約 2026.09.14 実装・公開事例
Primary sources

一次情報・参考リンク

About the author
SHAYOUWORLD

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