Never miss a Zwift ride with your friends again.
Zwift events aren't part of your day-to-day calendar — so it's easy to forget about a ride with friends when your calendar is full of work and family commitments. zwift-ical bridges that gap by pulling upcoming events from the Zwift public API and publishing them as iCal (.ics) feeds that any calendar application can subscribe to and auto-update.
All calendars are available at:
https://zwiftcal.beta.collom.co.uk
Subscribe to any .ics URL from that page directly in Google Calendar, Apple Calendar, Outlook, Thunderbird, or any other calendar application that supports iCal subscriptions.
Calendars are organised by sport and event type:
| Feed | URL pattern |
|---|---|
| All events | /zwift.ics |
| Cycling – group rides | /cycling/rides/events.ics |
| Cycling – races | /cycling/races/events.ics |
| Cycling – group workouts | /cycling/workouts/events.ics |
| Running – runs | /running/runs/events.ics |
| Running – races | /running/races/events.ics |
| Running – group workouts | /running/workouts/events.ics |
| By tag | /<sport>/tag/<tag>.ics |
Additional sports supported by Zwift (e.g. rowing, swimming) are automatically picked up as they appear in the API.
Zwift public API → Go app → iCal files → Cloudflare Pages → Your calendar
- The Go application (
cmd/zwiftcal) calls the Zwift upcoming events API. - Events are grouped by sport and event type (group ride, race, workout) as well as by any tags attached to the event.
- An
.icsfile is generated for each combination and written topublic/. - A static HTML index page is generated listing all available calendar feeds.
- The
public/directory is deployed to Cloudflare Pages via GitHub Actions.
Each calendar entry includes the Zwift world name in the event summary and location field:
Watopia, Richmond, London, New York, Innsbruck, Bologna TT, Yorkshire, Crit City, Makuri Islands, France, Paris, Gravel Mountain, Scotland.
GitHub Actions keeps the feeds fresh:
| Workflow | Schedule | Purpose |
|---|---|---|
| Build And Publish | Every hour (+ on push to main) |
Fetches events, regenerates .ics files, deploys to Cloudflare Pages |
| Regular committing | Every 5 days | Bumps .metadata to keep the repo active and Cloudflare cache warm |
| Unit Tests | On every PR / push | Runs go test ./... |
API limitation: The Zwift public API returns a maximum of 200 events per request and offers no pagination. The hourly schedule ensures the feeds stay as up-to-date as possible within this constraint.
- Open Google Calendar → Other calendars → From URL.
- Paste the
.icsURL (e.g.https://zwiftcal.beta.collom.co.uk/cycling/rides/events.ics). - Click Add calendar. Google will refresh it automatically.
- Go to Add calendar → Subscribe from web.
- Paste the
.icsURL and click Import.
- File → New Calendar Subscription…
- Paste the
.icsURL and click Subscribe.
Any application that supports iCal subscriptions (webcal:// or https://) can subscribe using the same URL.
- Go 1.24+
- Docker (optional, for container builds)
# Fetch events and generate the public/ directory
go run cmd/zwiftcal/main.goThe generated files will appear in public/.
go test ./...
# or via Make:
make testmake coverage # generates coverage.html
make coverage-report # prints per-function coverage to stdoutmake build # build image
make debug # build + drop into interactive shell
make publish # build + push to Docker Hubcmd/zwiftcal/ Main entrypoint – orchestrates fetch → generate → site
internal/
events/ Zwift API client (fetch.go) and types (types.go)
ical/ iCal generation (generate.go) and world map lookup (maps.go)
site/ Static site + iCal file writer and HTML template renderer
.github/workflows/ CI/CD: build, publish, scheduled refresh, unit tests
Dockerfile Multi-stage Docker image
Makefile Common dev tasks
Issues and pull requests are welcome. Please run go test ./... before submitting a PR.
MIT © David Collom