An iCalendar feed of the best events in the SF Bay Area — EDM, concerts, free things to do, and AI talks — refreshed every 6 hours. San Jose first, the rest of the Bay right behind it. One URL — subscribe from any calendar app.
Add to Google Calendar — one tap on Android or desktop. Google Calendar (app or web) opens straight to the "Subscribe" prompt, already filled in.
Or add a raw feed URL manually. Everything lives under
https://raw.githubusercontent.com/SiddanthEmani/kyros/main/:
| feed | what's in it |
|---|---|
events.ics |
everything, tagged [EDM] / [Live] / [AI] / [Free] / [Bay] |
feeds/edm.ics |
electronic — club nights and DJ shows |
feeds/music.ics |
all live music: concerts + EDM |
feeds/free.ics |
free events only |
feeds/ai.ics |
AI talks, demos, hackathons (the original kyros feed) |
- Apple Calendar
File → New Calendar Subscription→ paste URL - Google Calendar / Android use the one-tap link above, or on
desktop:
Other calendars → + → From URL→ paste URL - Outlook / Fantastical / anything else Add subscription by URL
kyros (κῦρος): authority, weight, signal over noise.
Luma ┐
TM ├─► classify ─► geo filter ─► dedup ─► rank ─► cap ─► 5 feeds
Fun. ┘ 5 buckets 50mi of SJ merge SJ first per cat every 6h
- Fetch — three sources, each isolated so a dead site can't take down a
refresh:
- Luma — AI talks, demos and hackathons. Scrapes the server-rendered discover HTML and paginates the JSON discover API, falling back through CORS relays when the runner IP is blocked.
- Ticketmaster — concerts and ticketed shows within 50 miles of downtown San Jose (SAP Center, Shoreline, The Ritz, Fox Oakland, Chase Center). Needs a free API key; skips itself without one.
- Funcheap — free and cheap events, region by region, South Bay first.
- Classify — each event lands in one or more of
ai,edm,concert,free,community. Anything that matches nothing is dropped, which is what keeps "Networking Drinks" out of the feed. - Geo — resolved to a Bay city and region by coordinates when a source gives them, else by city and venue names. Outside 50 miles of San Jose is dropped.
- Dedup — the same show arrives from more than one source. Matches merge, so one event keeps Ticketmaster's price and another source's genre tags.
- Rank & cap — per-category scoring plus a San Jose / South Bay boost, so SJ wins the caps while a strong SF show still makes it. Caps are per category, then a combined cap over the union.
- Write — the combined
events.icsplus the four category feeds.
If a refresh collapses (blocked IP, changed markup), the run fails loudly and keeps the existing feeds rather than publishing an empty calendar.
Concert coverage is much better with one. Grab a free key at
developer.ticketmaster.com (5,000
calls/day; a refresh uses ~10) and add it as a repository secret named
TICKETMASTER_API_KEY. Without it, that source logs no key, skipping and
everything else still builds.
pip install -r requirements-dev.txt
python run.py # build the feeds
python run.py --explain # ...and log the ranked keep-list per category
python run.py --dry-run --explain # fetch and rank, write nothing
python run.py --offline --explain # run the whole pipeline off committed fixtures
python -m pytest -q # offline parser + pipeline testsTwo flags exist for CI: --report PATH appends a markdown run report
(per-source counts, drop breakdown, top picks) — the PR check points it at
$GITHUB_STEP_SUMMARY — and --require-events N exits non-zero when fewer
than N events survive, which is how a silently-dead parser gets caught.
--ics-path puts the combined feed anywhere; the category feeds follow it
into the same directory, so a scratch run stays out of the repo:
python run.py --offline --ics-path /tmp/kyros/events.ics| key | default | meaning |
|---|---|---|
lookahead_days |
30 |
drop events past this horizon |
local_tz |
"America/Los_Angeles" |
IANA tz for every schedule check |
center |
SJ, radius_miles: 50 |
what counts as "around the area" |
region_boost |
south-bay 3.0, peninsula 1.5, sf/east-bay 0.5 |
the San Jose priority dial; San Jose proper gets +1.0 on top |
free_bonus |
1.0 |
score bonus for free events |
sources |
all true |
turn an individual source off |
cities / luma_categories |
SJ, SF, Oakland, Palo Alto / ["ai"] |
Luma discover scope |
ticketmaster_classifications |
["music"] |
Discovery classifications to query |
categories.<name>.cap |
8–15 | top-N per category |
categories.<name>.min_weekday_hour_local |
16–17 |
weekday events must start at/after this hour; weekends always pass |
combined_cap |
60 |
cap on the combined feed |
prefix_titles |
true |
[EDM]-style tags in the combined feed |
min_events_floor |
5 |
below this, keep the old feeds and fail the run |
Old flat configs still work: min_weekday_hour_local and
max_events_per_run are read and mapped onto the new shape.
run.py # CLI entry point
kyros/
config.py http.py model.py # config, fetching, the Event record
geo.py # Bay city registry, radius, SJ boost
classify.py rank.py # categories and per-category scoring
dedup.py ics.py # cross-source merge, feed writing
pipeline.py report.py # fetch -> filter -> rank -> write, run report
sources/ # luma, ticketmaster, funcheap
tests/ # offline, fixture-driven
scripts/fetch_fixtures.py # refresh fixtures when a site changes
config.json # user-editable
.github/workflows/
refresh.yml # 6-hourly schedule, commits the feeds
pr-check.yml # every PR commit: tests + a live dry run
events.ics feeds/*.ics # output (committed by CI)
It started as one calendar I could open on Sunday night and see every meaningful AI talk in the Bay for the coming weeks. The same filtering turns out to be exactly what the rest of a week needs — which warehouse party is worth the drive, which show at The Ritz is on sale, what's free downtown this weekend — without scrolling five sites that each cover a third of it.
MIT — fork it, run it for your city, ship your own feed.