【2026/09/20 更新】 API のレスポンス形式(data が複数件対応の配列 data: Address[] に統一されたこと)に合わせて、サンプルコードと型定義を更新しました。
ECサイト等のWebアプリや業務システムの開発で住所入力フォームを実装する際、「郵便番号を入力したら自動で都道府県や市区町村が入力される機能」は、ユーザー体験(UX)を向上させる必須で定番の機能です。
しかし、従来の郵便番号APIを利用する場合、以下のような手間が発生することがありました。
- 会員登録やAPIキーの発行・管理が面倒
-
.envなど環境変数の設定やデプロイ設定が必要 - 無料枠の制限やCORS設定、応答速度のばらつき
この記事では、事前登録およびAPIキーの発行が一切不要で、URLを呼び出すだけですぐに利用できるオープンな郵便番号・住所解決API pospita(ポスピタ) を使い、React(TypeScript)で住所自動入力フォームを作成する方法を解説します。
pospita(ポスピタ)とは?
pospita(ポスピタ)は、事前登録やAPIキーの発行なしで無料利用できる日本の郵便番号・住所インフラAPIです。
以下、特長です。
-
アカウント登録・APIキーが一切不要・即時動作: 会員登録や環境変数の設定なしで
fetchからそのまま利用可能 - 1日1,000リクエスト無料: 個人開発やプロトタイピング、本番サービスまでカバーする無料枠(IP単位)
- 高速レスポンス: Cloudflare Workers + D1 によるグローバルエッジ配信
- 充実した返却データ: 都道府県・市区町村・町域に加え、ひらがな・カタカナ読み、JIS市区町村コードを一括取得可能
-
AIエージェント対応:
/llms.txtやOpenAPI 3.0スキーマ、MCP (Model Context Protocol)を標準サポート
方法A:AIコーディングエージェントで作成する (Cursor / Claude / ChatGPT等)
CursorやClaude、ChatGPTなどのAIコーディングエージェントを使ってフォームを生成する場合、pospita は事前準備が不要なため、以下のプロンプトを一言渡すだけで完成します。
pospita API (https://pospita.jp/api/v1/addresses/{zipcode}) を使って、
郵便番号を入力したときに自動的に都道府県・市区町村・町域が入力される住所入力フォームをReact (TypeScript) で実装してください。
APIキーの登録は不要です。ハイフン除去処理とローディング表示も含めてください。
AIエージェントが .env の環境変数を気にする必要がないため、エラーを起こさず一発で動くコードを生成してくれます。
方法B:React (TypeScript) での手動実装サンプル
自分でコードを記述する場合の標準的なコンポーネント例です。
1. 住所データ型の定義
// types/address.ts
export interface PospitaAddressData {
zipcode: string;
prefecture: string;
city: string;
town: string;
full_address: string;
kana_prefecture: string;
kana_city: string;
kana_town: string;
kana_full: string;
roman_full: string;
jis_code: string;
is_office: number;
}
export interface PospitaApiResponse {
status: 'success' | 'error';
count: number;
data?: PospitaAddressData[];
message?: string;
}
2. 住所入力フォームコンポーネント
// components/AddressForm.tsx
import React, { useState } from 'react';
import { PospitaApiResponse } from '../types/address';
export const AddressForm: React.FC = () => {
const [zipcode, setZipcode] = useState('');
const [prefecture, setPrefecture] = useState('');
const [city, setCity] = useState('');
const [town, setTown] = useState('');
const [street, setStreet] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 郵便番号入力時のハンドラー
const handleZipcodeChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const val = e.target.value;
setZipcode(val);
setError(null);
// 数字以外の文字を除去
const cleanZip = val.replace(/[^0-9]/g, '');
// 7桁入力された時点でAPIを呼び出す
if (cleanZip.length === 7) {
setLoading(true);
try {
const res = await fetch(`https://pospita.jp/api/v1/addresses/${cleanZip}`);
if (!res.ok) {
throw new Error('住所の取得に失敗しました');
}
const result: PospitaApiResponse = await res.json();
if (result.status === 'success' && result.data && result.data.length > 0) {
const address = result.data[0];
setPrefecture(address.prefecture);
setCity(address.city);
setTown(address.town);
} else {
setError('該当する住所が見つかりませんでした');
}
} catch (err) {
setError('住所の検索中にエラーが発生しました');
} finally {
setLoading(false);
}
}
};
return (
<form style={{ maxWidth: '400px', margin: '0 auto', display: 'flex', flexDirection: 'column', gap: '16px' }}>
<div>
<label htmlFor="zipcode">郵便番号 (7桁)</label>
<input
id="zipcode"
type="text"
value={zipcode}
onChange={handleZipcodeChange}
placeholder="1000001 (ハイフン可)"
maxLength={8}
style={{ width: '100%', padding: '8px', marginTop: '4px' }}
/>
{loading && <span style={{ fontSize: '12px', color: '#666' }}>検索中...</span>}
{error && <p style={{ fontSize: '12px', color: 'red', margin: '4px 0 0' }}>{error}</p>}
</div>
<div>
<label htmlFor="prefecture">都道府県</label>
<input
id="prefecture"
type="text"
value={prefecture}
onChange={(e) => setPrefecture(e.target.value)}
style={{ width: '100%', padding: '8px', marginTop: '4px' }}
/>
</div>
<div>
<label htmlFor="city">市区町村</label>
<input
id="city"
type="text"
value={city}
onChange={(e) => setCity(e.target.value)}
style={{ width: '100%', padding: '8px', marginTop: '4px' }}
/>
</div>
<div>
<label htmlFor="town">町域</label>
<input
id="town"
type="text"
value={town}
onChange={(e) => setTown(e.target.value)}
style={{ width: '100%', padding: '8px', marginTop: '4px' }}
/>
</div>
<div>
<label htmlFor="street">番地・建物名</label>
<input
id="street"
type="text"
value={street}
onChange={(e) => setStreet(e.target.value)}
placeholder="1-1 〇〇ビル 101"
style={{ width: '100%', padding: '8px', marginTop: '4px' }}
/>
</div>
</form>
);
};
郵便番号検索 APIの仕様
pospita の郵便番号検索 API のエンドポイント仕様は、以下の通りとてもシンプルです。
リクエスト
GET https://pospita.jp/api/v1/addresses/1000001
レスポンス (JSON)
{
"status": "success",
"count": 1,
"data": [
{
"zipcode": "1000001",
"prefecture": "東京都",
"city": "千代田区",
"town": "千代田",
"full_address": "東京都千代田区千代田",
"kana_prefecture": "とうきょうと",
"kana_city": "ちよだく",
"kana_town": "ちよだ",
"kana_full": "とうきょうとちよだくちよだ",
"roman_full": "",
"jis_code": "13101",
"is_office": 0
}
]
}
かな・ふりがな入力が必要なフォームでも、kana_prefecture や kana_city をそのまま活用できます。
利用上限と仕様
- 認証: 不要(APIキー不要)
- 無料枠: 1日 1,000 リクエスト / IP
- バースト制御: 10秒あたり10リクエスト / IP
-
CORS: 全オリジン(
*)許可済み
まとめ
無料・登録不要の郵便番号 API pospita(ポスピタ)を利用することで、事前アカウント登録やAPIキー発行、環境変数の管理なしに、わずか数行のコードで郵便番号自動補完機能を実装できます。
個人開発のプロトタイピングや、Cursor/Claude等のAIエージェントに一気にフォームを作らせたい場面などで非常に有用です。ぜひお試しください!