Generate an ICS feed from a spreadsheet
An .ics file and an .ics feed are different things. A file is a one-time import you can never correct. A feed is a stable URL serving text/calendar that Apple Calendar, Google Calendar, Outlook, and anything else that speaks iCalendar can subscribe to and refetch, which means it stays correctable for as long as the schedule exists.
CalRelay generates that feed from your spreadsheet. Point it at a Google Sheet share link or a CSV URL (polled, so it updates on its own) or paste the table in (a snapshot you refresh by re-pasting). Map the columns once and you get a hosted feed URL plus a subscribe page.
Free plan, no credit card. No account needed to see your events.
Point CalRelay at your table
Paste a Google Sheets share link, a CSV URL, or the table itself. URL sources are polled on a schedule so the feed regenerates as the data changes. Pasted tables are a snapshot you update by pasting again.
Map columns and verify the preview
Title and date are required. Time, end time, location, and description are optional. The preview shows every generated event, and any row that can't be confidently parsed is held with its raw values. The feed never contains guessed data.
Grab the feed URL
You get the raw ICS URL plus webcal:// links wired into one-click subscribe buttons on a hosted page. The URL is permanent, so schedule changes flow through it and nobody needs to re-subscribe.
What an ICS feed URL actually is
It's an iCalendar (RFC 5545) document served over HTTPS with the text/calendar content type, at a URL that doesn't change. Calendar clients subscribe to it and refetch it periodically. The webcal:// scheme is the same https URL with a different prefix, so that clicking it opens the OS calendar's subscribe flow instead of downloading a file.
That refetch loop is the whole difference between a feed and a file. A downloaded .ics is frozen at export time. A feed reflects the source every time a client checks back, which is what lets the spreadsheet stay the source of truth.
The standards details that actually bite
Stable UIDs: every event's UID is deterministic and permanent, so when a row changes, subscribers' clients update the existing event instead of stacking a duplicate next to it. Generators that mint fresh UIDs on every export are the reason edited schedules turn into calendars full of doubles.
All-day events are emitted as VALUE=DATE, not as midnight-to-midnight timed events. The midnight version is the classic bug that shows your event on the wrong day for anyone in another timezone. Timed events carry explicit timezone handling, with the feed's timezone set by you rather than inferred.
Nothing is guessed. A row with an ambiguous or unparseable date is held out of the feed and shown to you with its raw values, because a plausible-looking wrong event is worse than a visibly missing one.
Refresh cadence: two hops, be honest about both
There are two delays between an edit and a subscriber's screen. First, CalRelay re-reads a URL source hourly on the free plan and every 15 minutes on Pro. Second, each calendar client refetches the feed on its own schedule. Apple Calendar and Outlook are typically prompt and configurable. Google Calendar refetches external feeds whenever it decides to, which can take up to 24 hours, and no feed provider can get around that.
If the source becomes unreadable (revoked sharing, renamed columns, a dead URL), the feed freezes at its last good state and you're emailed. Subscribers keep a valid calendar instead of getting an empty response.
Questions
What's the difference between webcal:// and https://?
Can I consume the feed URL from my own tools?
Does the feed use RRULEs for recurring events?
If I edit a row, do subscribers get a duplicate event?
What are the limits?
Your schedule already exists.
Turn it into a calendar your whole roster can subscribe to. Takes about three minutes.
Create your free feed