/** * ============================================================================= * QWHPI — Quebec Weekly Housing Price Index * Author : Simon-Pierre Boucher * Contact : contact@spboucher.ai * File : web/app/api-docs/page.tsx * Purpose : API docs — copyable examples per endpoint + live playground. * ============================================================================= */ "use client"; import { useState } from "react"; import { API_BASE } from "../../lib/api"; interface Endpoint { path: string; desc: string; example: string; } const ENDPOINTS: Endpoint[] = [ { path: "/v1/index", desc: "Full monthly history for one series (CSV via &format=csv)", example: "/v1/index?geography=quebec-city&type=condo&from=2023-01&to=latest" }, { path: "/v1/index/latest", desc: "Latest complete month for one series", example: "/v1/index/latest?geography=montreal&type=condo" }, { path: "/v1/stats", desc: "Derived metrics: peak, drawdown, CAGR, volatility, momentum, ranks, volumes", example: "/v1/stats?geography=quebec&type=all" }, { path: "/v1/stats/overview", desc: "One rich row per geography (heatmap data)", example: "/v1/stats/overview?level=region&type=all" }, { path: "/v1/compare", desc: "Aligned multi-series comparison, optional rebasing", example: "/v1/compare?series=montreal:condo,quebec-city:condo&rebase_period=2022-01" }, { path: "/v1/map", desc: "One metric per geography for a chosen month", example: "/v1/map?metric=yoy&level=region&period=2025-06" }, { path: "/v1/report", desc: "Automatic PDF report (1-5 series, &format=json for data)", example: "/v1/report?series=montreal:condo&format=json" }, { path: "/v1/vintages", desc: "First release vs current vintage (revision tracking)", example: "/v1/vintages?geography=quebec&type=all" }, { path: "/v1/geographies", desc: "Published hierarchy + coverage matrix", example: "/v1/geographies" }, { path: "/v1/meta", desc: "Model version, vintage, methodology summary", example: "/v1/meta" }, ]; function CopyButton({ text }: { text: string }) { const [copied, setCopied] = useState(false); return ( ); } export default function ApiDocs() { const [query, setQuery] = useState(ENDPOINTS[0].example); const [result, setResult] = useState(""); const [status, setStatus] = useState(""); const [running, setRunning] = useState(false); const run = async () => { setRunning(true); setStatus(""); const t0 = performance.now(); try { const res = await fetch(`${API_BASE}${query}`, { headers: { Accept: "application/json" }, }); const ms = Math.round(performance.now() - t0); const type = res.headers.get("content-type") ?? ""; setStatus(`${res.status} ${res.statusText} · ${ms} ms · ${type.split(";")[0]}`); if (type.includes("json")) { const body = await res.json(); setResult(JSON.stringify(body, null, 2).slice(0, 20000)); } else { const body = await res.text(); setResult(body.slice(0, 20000)); } } catch (e) { setStatus("network error"); setResult(String(e)); } setRunning(false); }; return ( <>

API

Every published series, statistic and report is available programmatically — confidence intervals and reliability grades on every observation, never hidden.{" "} Full OpenAPI reference ↗

Playground

setQuery(e.target.value)} onKeyDown={(e) => e.key === "Enter" && run()} style={{ flex: "1 1 340px", background: "var(--surface-1)", color: "var(--text-primary)", border: "1px solid var(--border-strong)", borderRadius: 10, padding: "8px 12px", fontSize: 13.5, fontFamily: "ui-monospace, monospace", }} />
{status && (

{status}

)} {result && (
{result}
)}

Endpoints

{ENDPOINTS.map((ep) => { const curl = `curl "${API_BASE}${ep.example}"`; return (
{ep.path}

{ep.desc}

{curl}
); })}
); }