About WarriorWatch
One page that answers “is the gym packed right now?” for the PAC and CIF, plus what's usual for this hour and the quietest time to go. It's a student project and is not affiliated with the University of Waterloo or Warrior Athletics & Recreation.
Where the number comes from
- Athletics publishes a live headcount for seven spaces on its Facility Occupancy page. We read that public page once a minute (scraping, not an official feed) and keep a copy of every change.
- The count is driven by WatCard tap-ins and tap-outs at the door, plus manual corrections by staff. People who leave without tapping out, or a counter that staff reset, show up as-is. We don't smooth or “fix” it.
- The official count doesn't change every minute. “Updated 4 min ago” means the number last changed 4 minutes ago, not when we last looked. A count that hasn't moved in a long time (common late at night, or in the Esports lounge) is usually just a quiet room.
- When the official page shows its own “results not live” banner, we show a warning too.
- Open / closed comes from the regular hours on each facility page. Holidays, varsity events and bookings can override them, so check the official calendar linked on each facility page.
What the labels mean
Labels are the share of the posted maximum occupancy, the same cut-offs the API uses:
- Quietunder 35% of capacity
- Moderate35–60%
- Busy60–85%
- Packed85% and up
“Usual” and how sure we are
“Usual” is the median count for that weekday and 15-minute slot, kept separately for regular term, reading week, exams and breaks. The shaded band is the middle half of past days. We always say how many days it's based on (“based on 3 Tuesdays”). With only a few weeks of data the band is wide and the “best times” are rough guesses; they get sharper every week.
Privacy
The source page only has totals, so that's all we store: a number per room per minute. No names, no WatCard data, no tracking cookies on this site.
Credit
Earlier student trackers showed this was worth doing: uwpac and PACked. The design bar is Waitz.
Public API
Free, JSON, CORS open, cached at the edge. Please keep it under a request a minute per client; the numbers don't change faster than that.
curl https://warriorwatch-api.warriorwatch-worker.workers.dev/api/liveWarriorWatch public API (v1)
Base: https://warriorwatch-api.warriorwatch-worker.workers.dev/api. All JSON. Access-Control-Allow-Origin: *. Edge-cached; Cache-Control headers tell you how long.
Facilities are addressed by slug (see FACILITY_SLUGS) or by the portal GUID.
| Endpoint | Query | Returns | Cache |
|---|---|---|---|
GET /api/live |
— | LiveResponse |
30 s |
GET /api/facilities |
— | FacilitiesResponse (all 44, with hours) |
1 h |
GET /api/history |
facility (required), from, to (ISO, default last 24 h), res = raw|5m|15m|1h (default 5m) |
HistoryResponse |
5 min |
GET /api/typical |
facility, weekday (0–6, default today), term (default: current) |
TypicalResponse |
1 h |
GET /api/best |
facility, date (YYYY-MM-DD, default today) |
BestResponse |
15 min |
GET /api/events |
facility, from, to |
EventsResponse |
15 min |
GET /api/health |
— | { ok, lastScrape, lastOkScrape, cards } |
none |
Errors: { "error": "message" } with 400/404/500. Rate limit: 60 req/min per IP, 429 beyond.
Types are the single source of truth in packages/contract/src/index.ts.
Embed
Every facility page has a copy-paste snippet. For example:
<iframe src="https://warriorwatch.pages.dev/embed/pac-free-weights" title="PAC free weights occupancy" width="320" height="120" style="border:0;border-radius:16px" loading="lazy"></iframe>