エンドポイント詳細リファレンス
築古不動産投資シミュレーターAPIの全エンドポイントを網羅した技術仕様書です。各エンドポイントの詳細な仕様、リクエスト・レスポンス例、エラーハンドリング、実装のベストプラクティスを詳しく解説します。
📋 API仕様概要
Base URL: https://kominka-apart-sim.vercel.app/api
Protocol: HTTPS only (TLS 1.2+)
Authentication: Bearer Token (JWT)
Content-Type: application/json
Charset: UTF-8
Rate Limiting: 1000 requests/hour
🔐 認証エンドポイント
POST /auth/oauth/token
説明: OAuth認証でアクセストークンを取得
リクエスト
POST /auth/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET&
redirect_uri=https://your-app.com/callback
レスポンス (200 OK)
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIwMjQtMDEifQ...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIwMjQtMDEifQ...",
"scope": "read:simulations write:simulations delete:simulations",
"user_id": "123e4567-e89b-12d3-a456-426614174000"
}
エラーレスポンス (400 Bad Request)
{
"error": "invalid_grant",
"error_description": "認証コードが無効または期限切れです",
"error_uri": "https://kominka-apart-sim.vercel.app/docs/errors#invalid_grant"
}
POST /auth/oauth/refresh
説明: リフレッシュトークンを使用してアクセストークンを更新
リクエスト
POST /auth/oauth/refresh
Content-Type: application/json
Authorization: Bearer CURRENT_ACCESS_TOKEN
{
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIwMjQtMDEifQ..."
}
レスポンス (200 OK)
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIwMjQtMDEifQ...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:simulations write:simulations delete:simulations"
}
👤 ユーザー管理エンドポイント
GET /users/me
説明: 現在のユーザー情報を取得
リクエスト
GET /users/me
Authorization: Bearer ACCESS_TOKEN
レスポンス (200 OK)
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "user@example.com",
"name": "田中太郎",
"avatar_url": "https://example.com/avatar.jpg",
"plan": "premium",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-17T10:30:00Z",
"preferences": {
"currency": "JPY",
"language": "ja",
"timezone": "Asia/Tokyo",
"email_notifications": true
},
"usage": {
"simulations_count": 15,
"calculations_count": 245,
"api_calls_this_month": 1250,
"storage_used_mb": 2.5
}
}
📊 シミュレーション管理エンドポイント
GET /simulations
説明: ユーザーのシミュレーション一覧を取得
クエリパラメータ
パラメータ
型
説明
デフォルト
page
integer
ページ番号
1
limit
integer
取得件数 (max: 100)
20
sort
string
ソート項目
created_at
order
string
ソート順 (asc/desc)
desc
property_type
string
物件種別フィルター
-
search
string
タイトル検索
-
リクエスト例
GET /simulations?page=1&limit=10&sort=updated_at&order=desc&property_type=house
Authorization: Bearer ACCESS_TOKEN
レスポンス (200 OK)
{
"simulations": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"title": "築30年木造戸建て投資",
"property_type": "house",
"property_price": 8000000,
"land_price": 3000000,
"building_price": 5000000,
"annual_rent": 960000,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-16T14:20:00Z",
"share_code": "abc123def456",
"is_public": false,
"results": {
"gross_yield": 12.0,
"net_yield": 9.12,
"monthly_cash_flow": 35200,
"annual_cash_flow": 422400,
"roi": 18.72,
"npv": 1850000,
"irr": 16.8,
"payback_period": 5.3
},
"tags": ["高利回り", "築古", "戸建て"]
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 25,
"total_pages": 3,
"has_next": true,
"has_prev": false
},
"filters": {
"property_type": "house",
"applied_filters": 1
}
}
POST /simulations
説明: 新しいシミュレーションを作成
リクエストボディ
POST /simulations
Content-Type: application/json
Authorization: Bearer ACCESS_TOKEN
{
"title": "築25年RC造アパート投資",
"property_type": "apartment",
"property_price": 25000000,
"land_price": 10000000,
"building_price": 15000000,
"annual_rent": 3600000,
"vacancy_rate": 8,
"expense_rate": 25,
"renovation_cost": 2000000,
"broker_fee_rate": 3,
"use_loan": true,
"loan_amount": 20000000,
"loan_interest_rate": 2.8,
"loan_term": 30,
"loan_type": "fixed",
"is_old_property": true,
"building_age": 25,
"structure_type": "rc",
"depreciation_method": "straight_line",
"building_ratio": 60,
"discount_rate": 5,
"holding_period": 10,
"exit_cap_rate": 8,
"prepayment_plans": [
{
"year": 3,
"amount": 5000000,
"source": "cash_flow"
}
],
"tags": ["アパート", "RC造", "築古"],
"memo": "駅から徒歩8分、周辺開発予定あり",
"is_public": false
}
レスポンス (201 Created)
{
"id": "789e0123-e89b-12d3-a456-426614174000",
"title": "築25年RC造アパート投資",
"created_at": "2024-01-17T11:00:00Z",
"share_code": "xyz789abc123",
"results": {
"gross_yield": 14.4,
"net_yield": 10.8,
"monthly_cash_flow": 125000,
"annual_cash_flow": 1500000,
"roi": 30.0,
"npv": 8450000,
"irr": 22.5,
"payback_period": 3.3,
"depreciation": {
"annual_amount": 1363636,
"remaining_years": 22,
"total_depreciation": 30000000
},
"tax_simulation": {
"personal_income_tax": 225000,
"corporate_tax": 135000,
"tax_savings": 90000
},
"sensitivity_analysis": {
"rent_impact": {
"-10%": { "roi": 21.0, "npv": 6800000 },
"+10%": { "roi": 39.0, "npv": 10100000 }
},
"vacancy_impact": {
"5%": { "roi": 27.5, "npv": 7650000 },
"15%": { "roi": 22.5, "npv": 5250000 }
}
}
}
}
GET /simulations/{id}
説明: 特定のシミュレーション詳細を取得
パスパラメータ
id: シミュレーションID (UUID)
クエリパラメータ
include: 含める関連データ (cash_flow, sensitivity, stress_test)
リクエスト例
GET /simulations/123e4567-e89b-12d3-a456-426614174000?include=cash_flow,sensitivity
Authorization: Bearer ACCESS_TOKEN
レスポンス (200 OK)
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"title": "築30年木造戸建て投資",
"property_type": "house",
"property_price": 8000000,
"land_price": 3000000,
"building_price": 5000000,
"annual_rent": 960000,
"vacancy_rate": 5,
"expense_rate": 20,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-16T14:20:00Z",
"user_id": "user123",
"share_code": "abc123def456",
"is_public": false,
"results": {
"basic_metrics": {
"gross_yield": 12.0,
"net_yield": 9.12,
"monthly_cash_flow": 35200,
"annual_cash_flow": 422400,
"roi": 18.72,
"total_initial_cost": 2400000
},
"advanced_metrics": {
"npv": 1850000,
"irr": 16.8,
"payback_period": 5.3,
"profitability_index": 1.77,
"break_even_vacancy_rate": 15.2
},
"cash_flow_projection": [
{
"year": 1,
"rental_income": 912000,
"operating_expenses": 182400,
"loan_payment": 384000,
"depreciation": 1136364,
"taxable_income": -790764,
"net_cash_flow": 345600,
"cumulative_cash_flow": 345600
},
{
"year": 2,
"rental_income": 912000,
"operating_expenses": 182400,
"loan_payment": 384000,
"depreciation": 1136364,
"taxable_income": -790764,
"net_cash_flow": 345600,
"cumulative_cash_flow": 691200
}
],
"sensitivity_analysis": {
"tornado_chart": [
{
"parameter": "annual_rent",
"impact_range": { "min": -3.2, "max": 3.2 },
"sensitivity_score": 0.85
},
{
"parameter": "vacancy_rate",
"impact_range": { "min": -2.1, "max": 0 },
"sensitivity_score": 0.65
}
],
"heatmap": {
"rent_vs_vacancy": [
{ "rent_change": -20, "vacancy_rate": 0, "net_yield": 5.1 },
{ "rent_change": -20, "vacancy_rate": 10, "net_yield": 3.8 },
{ "rent_change": 0, "vacancy_rate": 0, "net_yield": 9.1 },
{ "rent_change": 20, "vacancy_rate": 0, "net_yield": 13.1 }
]
}
}
},
"tags": ["高利回り", "築古", "戸建て"],
"memo": "駅徒歩12分、商業施設近く"
}
🔢 計算エンドポイント
POST /calculations/realtime
説明: リアルタイム計算(保存なし)
リクエストボディ
POST /calculations/realtime
Content-Type: application/json
Authorization: Bearer ACCESS_TOKEN
{
"property_price": 8000000,
"annual_rent": 960000,
"vacancy_rate": 5,
"expense_rate": 20,
"use_loan": true,
"loan_amount": 6000000,
"loan_interest_rate": 2.5,
"loan_term": 25,
"calculate_advanced": true,
"calculate_depreciation": true,
"calculate_tax": true
}
レスポンス (200 OK)
{
"calculation_id": "calc_789xyz123",
"timestamp": "2024-01-17T11:30:00Z",
"execution_time_ms": 45,
"results": {
"basic_metrics": {
"gross_yield": 12.0,
"net_yield": 8.64,
"monthly_cash_flow": 31200,
"annual_cash_flow": 374400,
"roi": 18.72,
"total_initial_cost": 2000000,
"monthly_loan_payment": 26847
},
"advanced_metrics": {
"npv": 1456000,
"irr": 15.2,
"payback_period": 5.8,
"profitability_index": 1.73,
"break_even_vacancy_rate": 12.8
},
"depreciation": {
"annual_amount": 1136364,
"method": "straight_line",
"remaining_years": 4,
"total_depreciation": 4545456
},
"tax_calculation": {
"taxable_income": -761964,
"personal_income_tax": 0,
"corporate_tax": 0,
"effective_tax_rate": 0,
"tax_savings": 152393
}
}
}
📈 分析エンドポイント
POST /simulations/{id}/sensitivity-analysis
説明: 感度分析を実行
リクエストボディ
POST /simulations/123e4567-e89b-12d3-a456-426614174000/sensitivity-analysis
Content-Type: application/json
Authorization: Bearer ACCESS_TOKEN
{
"parameters": [
{
"name": "annual_rent",
"range": { "min": -30, "max": 30, "step": 5 },
"unit": "percent"
},
{
"name": "vacancy_rate",
"range": { "min": 0, "max": 25, "step": 2.5 },
"unit": "percent"
},
{
"name": "expense_rate",
"range": { "min": 15, "max": 35, "step": 2.5 },
"unit": "percent"
},
{
"name": "loan_interest_rate",
"range": { "min": 1.0, "max": 5.0, "step": 0.25 },
"unit": "percent"
}
],
"target_metrics": ["net_yield", "roi", "npv", "irr"],
"confidence_level": 95,
"monte_carlo_runs": 10000
}
レスポンス (200 OK)
{
"analysis_id": "sens_456def789",
"simulation_id": "123e4567-e89b-12d3-a456-426614174000",
"created_at": "2024-01-17T12:00:00Z",
"execution_time_ms": 1250,
"results": {
"tornado_chart": [
{
"parameter": "annual_rent",
"parameter_label": "年間家賃収入",
"base_value": 960000,
"impact_on_roi": {
"min": -8.5,
"max": 8.5,
"range": 17.0
},
"impact_on_npv": {
"min": -850000,
"max": 850000,
"range": 1700000
},
"sensitivity_score": 0.92,
"rank": 1
},
{
"parameter": "loan_interest_rate",
"parameter_label": "ローン金利",
"base_value": 2.5,
"impact_on_roi": {
"min": -4.2,
"max": 6.8,
"range": 11.0
},
"impact_on_npv": {
"min": -420000,
"max": 680000,
"range": 1100000
},
"sensitivity_score": 0.73,
"rank": 2
}
],
"heatmap_data": {
"rent_vs_vacancy": [
{
"x": -20,
"y": 0,
"net_yield": 5.8,
"roi": 8.1,
"npv": 650000,
"color_intensity": 0.3
},
{
"x": 0,
"y": 5,
"net_yield": 8.6,
"roi": 17.2,
"npv": 1450000,
"color_intensity": 0.7
}
]
},
"monte_carlo": {
"statistics": {
"roi": {
"mean": 18.72,
"std_dev": 5.43,
"min": 2.1,
"max": 35.8,
"percentiles": {
"5": 9.8,
"25": 15.2,
"50": 18.7,
"75": 22.5,
"95": 28.1
}
},
"npv": {
"mean": 1856000,
"std_dev": 845000,
"min": -450000,
"max": 4200000,
"percentiles": {
"5": 520000,
"25": 1250000,
"50": 1850000,
"75": 2450000,
"95": 3200000
}
}
},
"risk_metrics": {
"probability_of_loss": 0.08,
"expected_shortfall": -125000,
"sharpe_ratio": 2.45,
"value_at_risk_95": 520000
}
}
}
}
🔍 検索エンドポイント
GET /search/simulations
説明: 高度な検索機能でシミュレーションを検索
クエリパラメータ
q: 検索キーワード
property_type: 物件種別 (house, apartment)
price_min: 物件価格の下限
price_max: 物件価格の上限
yield_min: 利回りの下限
yield_max: 利回りの上限
roi_min: ROIの下限
building_age_max: 築年数の上限
tags: タグ(カンマ区切り)
sort: ソート項目
order: ソート順
リクエスト例
GET /search/simulations?q=戸建て&property_type=house&price_min=5000000&price_max=12000000&yield_min=10&roi_min=15&tags=高利回り,築古
Authorization: Bearer ACCESS_TOKEN
レスポンス (200 OK)
{
"query": {
"keyword": "戸建て",
"filters": {
"property_type": "house",
"price_range": [5000000, 12000000],
"yield_min": 10,
"roi_min": 15,
"tags": ["高利回り", "築古"]
}
},
"results": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"title": "築30年木造戸建て投資",
"relevance_score": 0.92,
"matched_fields": ["title", "tags", "memo"],
"highlight": {
"title": "築30年木造戸建て投資",
"tags": ["高利回り", "築古", "戸建て"]
},
"property_type": "house",
"property_price": 8000000,
"results": {
"gross_yield": 12.0,
"net_yield": 9.12,
"roi": 18.72
},
"created_at": "2024-01-15T10:30:00Z"
}
],
"aggregations": {
"property_type": {
"house": 12,
"apartment": 8
},
"price_ranges": {
"5000000-8000000": 5,
"8000000-12000000": 10,
"12000000-15000000": 5
},
"yield_ranges": {
"10-12": 8,
"12-15": 7,
"15+": 5
}
},
"pagination": {
"page": 1,
"limit": 20,
"total": 20,
"total_pages": 1
}
}
❌ エラーレスポンス詳細
🚨 標準エラー形式
バリデーションエラー (400 Bad Request)
{
"error": {
"code": "VALIDATION_ERROR",
"message": "入力値が無効です",
"details": [
{
"field": "property_price",
"message": "物件価格は100万円以上である必要があります",
"received": 500000,
"expected": "≥ 1000000"
},
{
"field": "annual_rent",
"message": "年間家賃収入は必須です",
"received": null,
"expected": "number > 0"
}
]
},
"request_id": "req_123abc456def",
"timestamp": "2024-01-17T12:30:00Z"
}
認証エラー (401 Unauthorized)
{
"error": {
"code": "AUTHENTICATION_REQUIRED",
"message": "認証が必要です",
"details": [
{
"field": "authorization",
"message": "有効なBearerトークンが必要です"
}
]
},
"request_id": "req_789def123abc",
"timestamp": "2024-01-17T12:30:00Z"
}
権限エラー (403 Forbidden)
{
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"message": "このリソースにアクセスする権限がありません",
"details": [
{
"field": "simulation_id",
"message": "他のユーザーのシミュレーションにはアクセスできません",
"required_permission": "read:own_simulations"
}
]
},
"request_id": "req_456ghi789jkl",
"timestamp": "2024-01-17T12:30:00Z"
}
レート制限エラー (429 Too Many Requests)
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "レート制限に達しました",
"details": [
{
"field": "rate_limit",
"message": "1時間あたり1000リクエストの制限を超えました",
"limit": 1000,
"remaining": 0,
"reset_at": "2024-01-17T13:00:00Z"
}
]
},
"request_id": "req_789mno123pqr",
"timestamp": "2024-01-17T12:30:00Z"
}
📊 レスポンスヘッダー
標準ヘッダー
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Request-ID: req_123abc456def
X-Response-Time: 45ms
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1642694400
X-API-Version: v1
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
CORS ヘッダー
Access-Control-Allow-Origin: https://your-domain.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type, X-API-Key
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
🔄 ページネーション
ページネーション仕様
一覧系エンドポイントでは、一貫したページネーション形式を使用します。
標準ページネーション形式
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"total": 157,
"total_pages": 8,
"has_next": true,
"has_prev": true,
"next_page": 3,
"prev_page": 1
},
"links": {
"first": "/api/simulations?page=1&limit=20",
"prev": "/api/simulations?page=1&limit=20",
"next": "/api/simulations?page=3&limit=20",
"last": "/api/simulations?page=8&limit=20"
}
}
📝 実装のベストプラクティス
🎯 効率的なAPI利用
1. 適切なHTTPメソッドの使用
GET: データの取得(副作用なし)
POST: データの作成・複雑な操作
PUT: データの完全更新
PATCH: データの部分更新
DELETE: データの削除
2. 効率的なデータ取得
フィールド選択: ?fields=id,title,results.roi
関連データ: ?include=cash_flow,sensitivity
適切な制限: ?limit=50 (デフォルト: 20, 最大: 100)
3. エラーハンドリング
async function apiCall(endpoint, options) {
try {
const response = await fetch(endpoint, options);
// レート制限チェック
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
throw new Error(`レート制限。${retryAfter}秒後に再試行してください。`);
}
// 認証エラー
if (response.status === 401) {
await refreshToken();
return apiCall(endpoint, options); // 再試行
}
const data = await response.json();
if (!response.ok) {
throw new Error(data.error.message || '不明なエラー');
}
return data;
} catch (error) {
throw error;
}
}
🔗 関連リンク
MCP Server完全セットアップガイド
API認証・セキュリティガイド
API完全ガイド
HTTPステータスコード
HTTP/1.1 仕様