Wait Time Canada API Reference¶
Overview¶
Wait Time Canada APIs are served from Next.js route handlers in frontend/app/api/.
Base URL:
- Local:
http://localhost:3000/api - Production:
<PRODUCTION_BASE_URL>/api(when deployed)
Conventions¶
- Most routes use JSON responses.
- Many routes return
{ success, data }for success and{ success: false, error, message }for errors. - Some older routes use payload-first contracts (kept for compatibility).
- Shared anonymous read routes use short cache windows to reduce repeated Neon reads on the production VPS path.
Endpoint Catalog¶
Hospitals and comparability¶
GET /api/hospitals?province=ONGET /api/hospitals/[slug]/trends?period=24h|7d|30d|90d|6m|1yGET /api/compare?a=<hospital_id>&b=<hospital_id>
System and data quality¶
GET /api/healthGET /api/data-quality?hospital_id=<id>&days=30GET /api/data-quality?view=trend&source_id=<source_id>&days=30GET /api/data-quality?view=diff&source_id=<source_id>&compare_days=7GET /api/anomalies?source_id=<source_id>&days=7
Invalid GET /api/data-quality query combinations now return 400 instead of silently falling back to the aggregate system view. In particular:
view=trendandview=diffrequiresource_idview=hospitalrequireshospital_idhospital_idandsource_idcannot be combined- bare
source_idwithoutview=trend|diffis rejected
Analytics¶
GET /api/analytics/benchmarks?province=ON&period=24h|7d|30d[&hospital_id=<id>]GET /api/analytics/trends?province=ON&period=weekly|monthly&lookback=3m|6m|1yGET /api/analytics/regions?province=ON&period=24h|7d|30dGET /api/analytics/patterns?hospital_id=<id>&type=hour_of_day|day_of_week|monthlyGET /api/analytics/occupancy?province=ONGET /api/analytics/equity-summary?province=ON&period=24h|7d|30d
Equity layer¶
GET /api/equity-layer?province=ON
Public health resources¶
GET /api/resources?kind=facility|aed[&q=<term>&province=ON&latitude=<lat>&longitude=<lng>&radius=<km>&limit=<n>]GET /api/resources/alerts?limit=<n>GET /api/resources/system-context?province=ON[&q=<term>&limit=<n>]GET /api/resources/water-advisories?province=ON[&q=<term>&limit=<n>]GET /api/resources/aqhi?latitude=<lat>&longitude=<lng>
Export¶
GET /api/export?format=csv|json&granularity=raw|hourly|daily|weekly|monthlygranularity=hourlyis derived from raw measurements and limited to 30-day windows
Additional optional export params:
provincestart_dateend_dateinclude_methodology=true|false
Geolocation¶
GET /api/geolocation
Admin verification¶
GET /api/admin/hospitals/unverifiedPOST /api/admin/hospitals/[id]/verifyDELETE /api/admin/hospitals/[id]/verify
Key Contracts¶
Response caching and transfer guardrails¶
These routes are intentionally cache-friendly on the public frontend path:
GET /api/healthandGET /api/status: 2-minute shared cache windowGET /api/hospitals,GET /api/resources,GET /api/resources/alerts,GET /api/resources/system-context,GET /api/data-quality,GET /api/anomalies,GET /api/compare,GET /api/analytics/benchmarks,GET /api/analytics/trends,GET /api/analytics/regions,GET /api/analytics/occupancy,GET /api/analytics/equity-summary, andGET /api/analytics/patterns: 5-minute shared cache windowGET /api/methodology: 1-minute shared cache windowGET /api/hospitals/[slug]/trends: 10-minute shared cache window
Operational note:
- On the shared VPS runtime, these same read-heavy routes also use short-lived in-process response caching to cut repeat public transfer from Neon without changing data collection or storage policy.
GET /api/geolocationandGET /api/exportremainCache-Control: no-store.GET /api/resources/aqhiuses a 15-minute shared cache window with a live upstream fetch behind the server cache.GET /api/resources/water-advisoriesuses a 1-hour shared cache window with a live upstream fetch behind the server cache.
Health endpoint operational contract (M30)¶
GET /api/health remains backward-compatible and now includes structured scraper diagnostics.
Top-level fields:
healthy: booleandatabase: { status, latency_ms, pool_status }last_update: string | nullstale_threshold_minutes: numbersources: SourceHealth[]
Operational note:
- The live stale threshold is currently
120minutes, supplied by environment/config but exposed directly in the response so operators and clients can stay aligned.
Per-source diagnostics (SourceHealth) include:
- Existing:
source_id,source_name,last_run,status,error_message,measurements_count,age_minutes - Added:
last_success_run,last_success_measurements_count,last_error_run,last_error_category,last_error_stage,consecutive_failures,last_run_duration_ms
Failure categories currently emitted by backend heartbeat/classification logic:
upstream_unavailableparser_breakageinfra_runtimepersistence_failureunknown
Status and data-quality KPI cadence (D12)¶
GET /api/status and the aggregate GET /api/data-quality view now compute public KPI percentages against the active live scraper set only:
quebec-msssontario-healthalberta-ahsbc-phsa
Operational note:
- these aggregate percentages now reflect the current hourly GitHub Actions scraper cadence (
24expected runs/day), not the older 15-minute expectation model - hospital-level scrape counts and success rates are computed from distinct UTC hourly scrape windows, not raw measurement-row counts
- aggregate measurement counts are returned as actual counts, not reconstructed from percentage estimates
- historical
view=trend/view=diffresponses now includehistorical_annotationmetadata when the requested snapshot range spans both the legacy 15-minute expectation model (96expected runs/day) and the current hourly model (24expected runs/day) - the public
/data-qualitypage renders that annotation directly instead of implying long-range snapshot comparability that the historical records do not fully support
Comparability endpoint¶
GET /api/compare returns:
comparable: booleandivergence_brief: string | null- methodology fields for both hospitals
Public health resource contracts¶
GET /api/resources returns:
successcountdatameta.kindmeta.querymeta.source_statusmeta.source_catalog
GET /api/resources/alerts returns:
successcountdatameta.limitmeta.source_statusmeta.source_catalog
GET /api/resources/system-context returns:
successdata.dispatch_centresdata.paramedic_servicesmeta.querymeta.scopemeta.source_statusmeta.source_catalog
GET /api/resources/water-advisories returns:
successcountdatameta.querymeta.scopemeta.summarymeta.source_statusmeta.source_catalog
GET /api/resources/aqhi returns:
successdata(AQHIRecord | null)meta.latitudemeta.longitudemeta.source_statusmeta.source_catalog
Each source_status row includes:
source_idsource_nameprovenance_urllast_refreshed_atfreshness_state(show | warn | suppress)
Each source_catalog row includes:
source_iddomainsource_nameconnector_typeaccess_routelicense_reuse_statusattribution_requirementupdate_cadencerecommended_usage_modepublic_methodology_noteprovenance_urllast_verified_atlast_refreshed_atfreshness_state
Operational note:
- Facility rows are reference-directory data only and should not be interpreted as live operational availability.
- AED rows are OSM-backed fallback data and remain explicitly crowdsourced/incomplete.
- Ontario EMS system-context rows are background context only and should not be interpreted as live dispatch availability or routing guidance.
- Ontario drinking-water advisories are scoped to active long-term advisories on public systems on reserve and should not be interpreted as a complete map of all Ontario water advisories.
- AQHI responses may intentionally return
data: nullwhen the upstream source freshness state issuppress.
Occupancy availability contract¶
GET /api/analytics/occupancy returns one of:
status: "available"status: "no_reporting_data"status: "not_available_yet"
This ensures missing source fields are explicit, not silently treated as zero.
Equity availability contract¶
GET /api/analytics/equity-summary returns one of:
status: "ready"status: "no_reporting_data"status: "not_available_yet"
When status: "ready", the payload also includes:
sensitivity_analysis(threshold table, default 10/20/30/40 km)uncertainty(bootstrap percentile 95% intervals, descriptive only)methodologymetadata including:interpretation: "descriptive_association_only"causal_inference: falsecensus_income_reference_yearwait_aggregation_period- temporal alignment note
GET /api/equity-layer returns setup guidance when province data is not scaffolded yet, and for successful ON responses includes metadata:
reference_yearinterpretationcausal_inferencetemporal_alignment_notesource_fileoptimized_geometry
Layer loading behavior is optimized-first with canonical fallback:
ontario-equity-layer.optimized.geojson(preferred when present)ontario-equity-layer.geojson(fallback)
Analytics freshness notes¶
GET /api/analytics/patterns?type=hour_of_dayderives its hourly view from bounded raw measurements rather than precomputed hourly aggregates.GET /api/export?granularity=hourlyis intended for bounded recent windows only and returns an error for oversized hourly requests.
Example Requests¶
# Hospitals in Ontario
curl "http://localhost:3000/api/hospitals?province=ON"
# Compare two hospitals
curl "http://localhost:3000/api/compare?a=ca-on-ottawa-general&b=ca-qc-chum"
# Benchmarks
curl "http://localhost:3000/api/analytics/benchmarks?province=ON&period=7d"
# Province trend
curl "http://localhost:3000/api/analytics/trends?province=ON&period=monthly&lookback=6m"
# Regions
curl "http://localhost:3000/api/analytics/regions?province=ON&period=7d"
# Export aggregated monthly data
curl "http://localhost:3000/api/export?format=json&granularity=monthly&province=ON"
Data Use and Interpretation¶
- Use methodology tags when interpreting or comparing hospitals.
- Do not interpret cross-province comparisons as directly comparable unless ontology dimensions match.
- This platform is informational and operational; it is not a triage or medical advice service.