- Reading
- Evidence and data
- Developer API
Every figure on this site is one free JSON call away.Developer API
Every figure here is versioned JSON. Free to use, with or without a key.Every figure here is versioned JSON. Free to use, with or without a key.
Base address https://otranspo.ca/api/public/v1
Free, key optional · 1,000 a day, 60 a minute · code MIT · version 1.0.0
- Access
Free
key optional
- Rate limit
1,000 a day
60 a minute; more on Developer
- Code licence
MIT
data keeps its source’s terms
- Version
1.0.0
stable · 180 days’ notice
Plans
Free
1,000 calls a day, 60 a minute. No key needed; a free key counts your calls separately from everyone else on your network.
Credit ‘Data: OTranspo (otranspo.ca)’ wherever you show the data.
Developer
$10 a monthor $100 a year (CAD)
- 100,000 calls a day, 600 a minute
- Bulk downloads and full history
- Support by email
Journalists, students and council staff: the free tier is yours. Write to us if you need more for a story or a study.
How to send a key
curl -H "Authorization: Bearer otr_…" https://otranspo.ca/api/public/v1/route-statesTsi niió:re
Each answer carries X-RateLimit-Remaining (this minute) and X-RateLimit-Daily-Remaining (today, UTC). Over the limit you get 429 with the time it resets.
One call, start to finish
Request
curl https://otranspo.ca/api/public/v1/route-statesThe response is the real one from this site, fetched when you ask.
Response, shortened Schema
Running…
Every resource, 39
Paths start at /api/public/v1 unless written in full. Open any row for its HTTP statuses, error codes, access, uncertainty and schema.
Index and contractstart here2
API indexGET /api /public /v1—JSON
HTTP status
200
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
endpoints.id · endpoints.path
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
OpenAPI 3.1 contractGET /openapi.json—JSON
HTTP status
200
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
operationId · path
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Service ó:nenwhat is happening this minute5
Cancellations posted nowGET /current-cancellations—JSON
HTTP status
200 · 400 · 503
Error reason codes
unsupported_query 400 · current_cancellations_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
trips.tripId · trips.routeId
Source and uncertainty
Only explicit CANCELED/DELETED notices for timetable trips in the current snapshot are listed. Empty or unavailable feeds do not prove that no trips were cancelled; outside-timetable notices may be withheld.
Route states: scheduled vs seenGET /route-states—JSON
HTTP status
200 · 503
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
operatorId · routeId
Source and uncertainty
This is not an operator alert. Routes without published positions use positions_not_published; stale or missing feeds return unavailable.
Incidents, in English and FrenchGET /incidents—JSON
HTTP status
200 · 503
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
publicId · slug · updates.id
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
One official alertGET /alerts /{alertId}format=json|csv · alertIdJSON · CSV
HTTP status
200 · 400 · 503
Error reason codes
invalid_alert_id 400 · unsupported_format 400 · alert_not_found 400 · alert_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
alert.id · alert.operatorId
Source and uncertainty
The notice is the operator’s own wording. Kept wordings start when we first saw the notice, not when the operator posted it, and an empty list does not prove the notice never changed.
Station boardGET /stations /{stationId}format=json|csv · stationIdJSON
HTTP status
200 · 400 · 404 · 503
Error reason codes
invalid_station_id 400 · unsupported_format 400 · station_not_found 404 · station_board_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
station.id · lines.routeRecordId · departures.tripId
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Evidence and reportsthe figures behind every page11
Reliability evidenceGET /reliability-evidenceformat=json|csvJSON · CSV
HTTP status
200 · 400 · 503
Error reason codes
unsupported_format 400
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
routeId · window
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Ohahà:'a kahiatónhseraGET /routes /{routeId} /evidenceformat=json|csv · routeIdJSON
HTTP status
200 · 400 · 404 · 503
Error reason codes
invalid_route_id 400 · unsupported_format 400 · route_not_found 404 · route_evidence_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
route.routeId
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Route service by dayGET /routes /{routeId} /service-evidencewindow=day|week|month|28d · date · format=json|csv · routeIdJSON
HTTP status
200 · 400 · 404 · 503
Error reason codes
invalid_route_id 400 · unsupported_window 400 · invalid_service_date 400 · unsupported_format 400 · route_not_found 404 · route_service_evidence_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
routeId · window
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Station or ward serviceGET /places /{kind} /{placeId} /service-evidencedate · kind=station|ward · placeIdJSON
HTTP status
200 · 400 · 503
Error reason codes
unsupported_place_kind 400 · invalid_place_id 400 · unsupported_query 400 · invalid_service_date 400 · place_service_evidence_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
kind · placeId · operatorId
Source and uncertainty
Observation and cancellation completeness are explicitly unverified; no-position candidate counts remain null and comparison eligibility is false.
Evidence record downloadGET /evidence /{evidenceId}format=json|csv · evidenceIdJSON · CSV
HTTP status
200 · 400 · 404
Error reason codes
unsupported_format 400 · not_found 404
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
evidenceId
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Ward scorecardsGET /ward-scorecardsformat=json|csvJSON · CSV
HTTP status
200 · 503
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
snapshotId · wardId
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Past scorecard editionsGET /historical-scorecardssnapshotId=historical-scorecards:<sha256> · routeIdJSON
HTTP status
200 · 400 · 404 · 422
Error reason codes
snapshot_id_required 400 · invalid_route_id 400 · invalid_parameters 400 · snapshot_not_found 404 · route_detail_not_published 422 · route_not_in_snapshot 404
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
snapshotId · routeId
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Service report archiveGET /service-report-archivekindJSON
HTTP status
200 · 400 · 503
Error reason codes
invalid_archive_query 400 · archive_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
kind · period
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Prediction accuracyGET /prediction-calibration—JSON
HTTP status
200 · 503
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
operatorId · routeId · routeContext · horizonBucket
Source and uncertainty
Later observed arrival is a comparison, not ground truth or a validated prediction interval; independent validation is false.
Selected service reportGET /api /insights /reports /selected?period={period}&date={YYYY-MM-DD}period=day|week|month · date=YYYY-MM-DD · operator=all|oc_transpo|sto · routeJSON
HTTP status
200 · 400 · 503
Error reason codes
invalid_selection 400 · selected_report_unavailable 503 · invalid_route_hour_selection 400 · route_hours_unavailable 503
Access and rate limit
Anonymous; no public-api limit is configured for this path.
Stable identifiers
period · date · operator · method
Source and uncertainty
Without route, the selected-report contract is unchanged. With route, the selected-route-hours contract returns at most48 method-separated local-hour prediction groups, preserving requested report operator separately from routeOperator. These mutable retained predictions do not prove physical arrivals, complete archive coverage, zero service or an immutable publication.
Prediction comparison inputsGET /api /insights /reports /selected /comparison-evidence?period={period}&date={YYYY-MM-DD}period=day|week|month · date=YYYY-MM-DD · operator=all|oc_transpo|stoJSON
HTTP status
200 · 400 · 503
Error reason codes
invalid_comparison_evidence_selection 400
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
payload.edition · payload.selection · checksum.value
Source and uncertainty
This opt-in export preserves the exact captured route/day read results, declared calendar, comparison refusals and measured source-code manifest. It is bounded to 20,000 input rows and 8 MiB and does not claim an atomic database snapshot, statistical independence, confidence intervals, physical service delivery, causation, equivalence or model eligibility. Schema validation alone does not verify checksum or replay; consumers must perform those checks separately.
Example call
GET /api/insights/reports/selected/comparison-evidence?period={period}&date={YYYY-MM-DD}
History and replaywhat already happened6
Past cancellations and gapsGET /historical-cancellationswindow=day|week|28d|month · date · assessment=noneJSON
HTTP status
200 · 400 · 503
Error reason codes
unsupported_query 400 · unsupported_window 400 · invalid_service_date 400 · unsupported_assessment 400 · historical_cancellations_unavailable 503 · historical_cancellation_limit_exceeded 400
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
period · records.tripId
Source and uncertainty
Historical polling completeness is unverified and schedule matching may be unassessed; unknown coverage is not zero cancellations.
City replayGET /replaydate · routeFamily · operator · fromMinute · minutes · summaryJSON
HTTP status
200 · 400 · 503
Error reason codes
date_required 400 · invalid_route_family 400 · invalid_operator 400 · replay_positions_unavailable 503 · published_replay_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
date · routeFamily · operator
Source and uncertainty
Replay is historical and never live; only captured/published archive evidence is returned.
Vehicles seen, by dayGET /fleet-historydate · operator=all|oc_transpo|sto · route · feedIdJSON
HTTP status
200 · 400 · 503
Error reason codes
invalid_fleet_history_lookup 400 · invalid_fleet_history_date 400 · invalid_fleet_history_operator 400 · fleet_history_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
serviceDate · operator · route
Source and uncertainty
Retained feed observations are not a physical fleet inventory; missing retention is not zero service.
Feed vehicle IDs, by dayGET /fleet-history /feed-idsdate · operator=all|oc_transpo|sto · cursorJSON
HTTP status
200 · 400 · 503
Error reason codes
invalid_fleet_feed_id_selection 400 · invalid_fleet_history_date 400 · invalid_fleet_history_operator 400 · invalid_fleet_feed_id_cursor 400 · fleet_feed_id_census_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
date · operator · digest · collisionOrdinal
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
One vehicle ID over timeGET /vehicles /{vehicleId} /visibility-historyformat=json|csv · vehicleIdJSON
HTTP status
200 · 400 · 404 · 503
Error reason codes
invalid_vehicle_id 400 · unsupported_format 400 · vehicle_history_unavailable 503 · vehicle_history_not_found 404
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
vehicleId
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Vehicles seen, in bulkGET /bulk /fleet-historyfrom · to · operator=all|oc_transpo|sto · route · feedIdJSON
HTTP status
200 · 400 · 401 · 403 · 429 · 503
Error reason codes
unsupported_query 400 · invalid_bulk_range 400 · invalid_fleet_history_lookup 400 · invalid_fleet_history_operator 400 · fleet_history_unavailable 503 · api_key_required 401 · invalid_api_key 401 · plan_required 403 · rate_limited 429
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
serviceDate · operator · route
Source and uncertainty
Each line is the fleet-history evidence for one seven-day window; retained feed observations are not a physical fleet inventory and missing retention is not zero service.
Timetables and offlinethe published timetable6
Timetable for one routeGET /transit-planning /{routeId}date · routeIdJSON
HTTP status
200 · 400 · 503
Error reason codes
invalid_route_or_date 400 · use_history_for_past_dates 400 · planning_baseline_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
routeId · serviceDate
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Timetable for the networkGET /transit-planning /networkdateJSON
HTTP status
200 · 400 · 503
Error reason codes
invalid_parameters 400 · invalid_service_date 400 · network_baseline_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
serviceDate
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Street path between two pointsPOST /transit-planning /street-pathbodyJSON
HTTP status
200 · 400 · 503
Error reason codes
invalid_body 400 · invalid_segment 400
Access and rate limit
Anonymous, trusted origin required; 60 a minute when distributed rate limiting is on.
Stable identifiers
from · to
Source and uncertainty
Path may be unavailable when routing source data is absent; no path is not evidence of no accessible route.
Offline timetable packsGET /offline-pack—JSON
HTTP status
200 · 503
Error reason codes
offline_pack_not_published 503 · offline_pack_unavailable 503
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
packId · digest
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Offline pack manifestGET /offline-pack /manifestpackJSON
HTTP status
200 · 404 · 503
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
packId · shards.id
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Offline pack pieceGET /offline-pack /shards /{id}idbytes
HTTP status
200 · 404 · 503
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
id · digest
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Registers and policywhat we changed and why6
Corrections registerGET /correctionsformat=json|csvJSON · CSV
HTTP status
200 · 400
Error reason codes
unsupported_format 400
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
records.id · records.revision
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Policy registerGET /policy-register—JSON
HTTP status
200
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
records.id · records.revision
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Accessibility assuranceGET /accessibility-assurance—JSON
HTTP status
200
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
evidence.id · remediationLog.id
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Regional coverage and freshnessGET /regional-coverage—JSON
HTTP status
200 · 503
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
operators.operatorId
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Notification and help channelsGET /journey-service-policy—JSON
HTTP status
200
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
contractVersion · channels.channel
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Vehicle presentation checkGET /fleet-presentation—JSON
HTTP status
200 · 503
Error reason codes
—
Access and rate limit
Anonymous access; 60 a minute when distributed rate limiting is on.
Stable identifiers
evidence.operatorSamples.operatorId
Source and uncertainty
Read the response status, evidenceState, source timestamps, limitations and gaps; absence is not proof of zero.
Sharinglinks and social cards3
Access and use
Access and useLimits, caching, versions, privacy and licence
- Access
- Every GET resource is public and free to use, with or without a key. Bulk and history resources need a Developer key. The two POST resources (making a share link, drawing a street path) only accept requests from otranspo.ca itself.
- Rate limit
- 1,000 calls a day and 60 a minute, with or without a free key; 100,000 a day and 600 a minute on the Developer plan. Read the X-RateLimit-* headers on every answer; X-RateLimit-Status says when limits are degraded.
- Keys
- Send a key as Authorization: Bearer otr_… or X-API-Key. Never in the address. A wrong, revoked or expired key gets 401, never a keyless answer.
- Caching
- The index is kept 30 seconds by your browser and 5 minutes at the edge. Live resources such as cancellations are sent no-store. Offline timetable pieces never change once published.
- Versions
- Each response names its schema and schemaVersion. A breaking change gets a new /v2 path, with 180 days’ notice in Deprecation, Sunset and Link headers.
- Sahsénna ne í:se
- Do not send personal data. Coordinates sent to the street path are used for that request only, never logged or stored. Responses never hold account IDs, trip searches, email or network addresses, or internal evidence links.
- Unknown is not zero
- A response can be unavailable even when the resource exists. Read status, evidenceState, source times, coverage and limitations. Route states compare the timetable with buses seen; they are not operator alerts.
- Licence
- Code: MIT. OC Transpo and STO data stay under their own open-data terms and need credit. Our figures: CC BY 4.0, credit otranspo.ca.
- Not done yet
- No SDK is published, no second city is supported, and partner tenant-key access is not open. This page does not claim them.