Ottawa recreation schedule data

This page provides an unofficial dataset of the City of Ottawa drop-in recreation schedules, updated daily.

If you encounter any problems or inaccurate data, please open an issue or email me.

The data was last updated on 2026-07-14.

Usage

This section covers some common questions and pitfalls for using the data. Also see the schema further down this page for more detailed information about the available fields.

Data updates

I recommend polling for dataset updates every 10-60 minutes to ensure you catch updates soon after they occur. You can use standard ETag-based caching to avoid redownloading the same dataset. You should use a descriptive user-agent when fetching.

Facility keys

Facilities are keyed by their URL. Obviously, this may change, but within a single dataset, you should use this for cross-referencing. If you need a stable identifier for a facility over time, you'll need to figure that out yourself. I personally use either the first part of the address, or a an accent-stripped and sluggified facility name.

Accuracy

This dataset is scraped and parsed from the city website in a deterministic manner using a carefully designed hand-written script to ensure all times on the website are included and valid, emitting errors for any potential issues. The city has made typos and mistakes in the website on a semi-regular basis. You should display the text in the "errors" field in the dataset, if any, to avoid silently missing relevant content. I check these daily and report them to the city when necessary.

In some cases, the StartTime/EndTime may be empty due to the time being unparseable. If this is the case, you should fall back to displaying the RawTime.

The city cancels drop-in sessions on a regular basis. Usually, they update the site at least a day before (occasionally, they don't post them at all), which will cause them to appear in the facility special-hours and/or the schedule group schedule-changes. If these are present, display them to the user.

Holiday schedules may overlap with time ranges of regular schedules. It is not possible to disambiguate them unambiguously, so do not try. Showing the schedule-changes or facility notifications/special-hours if present is the best way to notify users.

The ReservationRequired field errs on the side of being true if the city website is ambiguous. Keep this in mind if designing user interfaces around it.

I'm working on an extended dataset which labels portions of the special-hours/notifications/schedule-changes on a best-effort basis so ones not relevant to a specific instance of a session can be hidden to reduce the amount of warnings which would end up needing to be displayed. I'm already using this on the main ottrec website, but I still need to design a stable schema to expose this in a usable, non-error-prone, and future-proof way.

Raw fields

The raw fields in the dataset contain the raw text from the city website most of the other fields are derived from. They have minimal whitespace and unicode normalization applied, but are otherwise unchanged.

You may find it useful to use the raw fields internally for grouping or for displaying to the user. This is okay, but be warned that they may change (or be removed entirely) if the city changes the website structure, and do not attempt to parse them (any parseable information from them is already exposed in the other stable fields).

Other stuff

In addition to the required attribution text, I'd appreciate a link to https://ottrec.ca if or a mention on social media you use this data in an app or website.

If you make something, I'm also always happy to hear about it and take a look, so feel free to send me an email.

Preview

You can view all schedules on a single webpage.

If you're not a developer and just looking for recreation schedules, see ottrec.ca.

HTML

Simplified dataset

This format contains drop-in recreation facilities and activity times, with fully-parsed dates/times, cleaned titles, and inferred reservation requirements. It is available as JSON or CSV, along with a fully-documented stable schema.

Facility facility facilityfacility information
URL url facility_urlcity of ottawa facility page url
ScrapedAt scrapedAt facility_scraped_atdate (YYYY-MM-DD) the date for the facility was scraped at
Name name facility_namename of the facility
Address address facility_addressthe address of the facility
Longitude longitude facility_longitudefacility longitude (may not be set if geocoding failed)
Latitude latitude facility_latitudefacility latitude (may not be set if geocoding failed)
Region region facility_regionfacility location region (may not be set if geocoding failed)
Sector sector facility_sectorfacility location sector (may not be set if geocoding failed)
SpecialHoursHTML specialHoursHtmlId facility_special_hours_html_idhtml for special hours
NotificationsHTML notificationsHtmlId facility_notifications_html_idhtml for notifications
Activity activity activityactivity information
FacilityURL facilityUrl facility_urlfacility url for the activity
StartDate startDate activity_date_startstart date (YYYY-MM-DD), inclusive (may not be set if parsing failed or there's no range)
EndDate endDate activity_date_endend date (YYYY-MM-DD), inclusive (may not be set if parsing failed or there's no range)
Weekday weekday activity_weekdayweekday (lowercase, long-form) or single date (YYYY-MM-DD) (may not be set if parsing failed)
StartTime startTime activity_time_startstart time (HH:MM), inclusive (may not be set if parsing failed)
EndTime endTime activity_time_endend time (HH:MM), exclusive (may not be set if parsing failed)
Name name activity_nameactivity name, normalized
ReservationRequired reservationRequired activity_reservation_requiredwhether reservation is required, best-effort
ReservationLinks reservationLinks activity_reservation_linksreservation urls (comma-separated for csv)
ExceptionsHTML exceptionsHtmlId activity_exceptions_html_idhtml for schedule exceptions
RawScheduleGroup rawScheduleGroup activity_raw_groupraw schedule group text (this field is not stable)
RawSchedule rawSchedule activity_raw_scheduleraw schedule caption text (this field is not stable)
RawDay rawDay activity_raw_dayraw schedule activity day (this field is not stable)
RawActivity rawActivity activity_raw_activityraw schedule activity label (this field is not stable)
RawTime rawTime activity_raw_timeraw schedule activity time (this field is not stable)
Error error errorerrors which occured while scraping the facility pages
FacilityURL facilityUrl facility_urlfacility url the error occured while scraping
Error error errorerror message
HTML html htmllonger snippets of html referenced from facility/activity
ID id idindex for cross-referencing, not stable
HTML html htmlraw html
Attribution attribution attributionattribution
Text text texttext

Raw dataset

This format is designed to match the structure of the website as closely as possible with minimal processing and no assumptions. This make it more complex to use correctly, but preserves all information from the website.

API

Version specs

latest
Newest available data.
latest-N
N versions before the newest available data.
YYYY-MM
YYYY-MM-DD
Newest available data at the end of the specified date.
ID
Canonical reference to a specific revision of the data.

Export

/export/schema.json
/export/schema.csv
The current schema for the simplified dataset.
/export/:spec.json
/export/:spec.csv.zip
Download a simplified dataset. Historical data may not be available beyond a cut-off date if the underlying data format changes too much.

The API is stable, but the data schema is subject to change if required.

Cross-origin requests are allowed.

Raw (v1)

/v1/?limit=N&after=ID&revisions=true|false
A JSON array of available data, in descending order by date/revision. If revisions is not set to true, only the most recent revision for each date will be listed. The default and maximum per-page limit is subject to change. Each one is uniquely identified by the ID. The revision is incremented for every additional update to the data for a specific date. You can call this endpoint repeatedly with the last ID on the previous page until an empty array is returned.
[{"id": string, "revision": integer,"updated": date-rfc3339}]
/v1/:spec
/v1/:spec/:format
Download a raw dataset in the specified format. Currently, the valid formats are proto, pb, textpb, or json.

If the protobuf schema changes in a way which breaks backwards/forwards-compatible decoding, a new /v2/ api will be introduced for data beyond that point.

Cross-origin requests are allowed.

Historical data

VersionSimplifiedRaw
July 14, 2026 json csvproto pb textpb json
July 13, 2026 json csvproto pb textpb json
July 13, 2026 json csvproto pb textpb json
July 12, 2026 json csvproto pb textpb json
July 12, 2026 json csvproto pb textpb json
July 11, 2026 json csvproto pb textpb json
July 11, 2026 json csvproto pb textpb json
July 10, 2026 json csvproto pb textpb json
July 10, 2026 json csvproto pb textpb json
July 9, 2026 json csvproto pb textpb json
July 9, 2026 json csvproto pb textpb json
July 8, 2026 json csvproto pb textpb json
July 8, 2026 json csvproto pb textpb json
July 7, 2026 json csvproto pb textpb json
July 7, 2026 json csvproto pb textpb json
July 6, 2026 json csvproto pb textpb json
July 6, 2026 json csvproto pb textpb json
July 6, 2026 json csvproto pb textpb json
July 5, 2026 json csvproto pb textpb json
July 5, 2026 json csvproto pb textpb json
July 4, 2026 rev 2json csvproto pb textpb json
July 4, 2026 json csvproto pb textpb json
July 4, 2026 json csvproto pb textpb json
July 3, 2026 json csvproto pb textpb json
July 3, 2026 json csvproto pb textpb json
July 2, 2026 json csvproto pb textpb json
July 2, 2026 json csvproto pb textpb json
July 1, 2026 json csvproto pb textpb json
July 1, 2026 json csvproto pb textpb json
June 30, 2026 json csvproto pb textpb json
June 30, 2026 json csvproto pb textpb json
June 29, 2026 json csvproto pb textpb json
June 29, 2026 json csvproto pb textpb json
June 28, 2026 json csvproto pb textpb json
June 28, 2026 json csvproto pb textpb json
June 27, 2026 json csvproto pb textpb json
June 27, 2026 json csvproto pb textpb json
June 26, 2026 json csvproto pb textpb json
June 26, 2026 json csvproto pb textpb json
June 25, 2026 json csvproto pb textpb json
June 25, 2026 json csvproto pb textpb json
June 24, 2026 json csvproto pb textpb json
June 24, 2026 json csvproto pb textpb json
June 23, 2026 json csvproto pb textpb json
June 23, 2026 json csvproto pb textpb json
June 22, 2026 json csvproto pb textpb json
June 22, 2026 json csvproto pb textpb json
June 21, 2026 json csvproto pb textpb json
June 21, 2026 json csvproto pb textpb json
June 20, 2026 json csvproto pb textpb json

Showing the last 50 versions. Use the API to access older data.

License

This data has been scraped and redistributed with permission from the City of Ottawa, and can be used freely as long as the attribution text in the provided files is displayed where the data is used.